關(guān)于web項(xiàng)目鑒權(quán)的一些思考與三種實(shí)現(xiàn)方式

引言

項(xiàng)目登陸校驗(yàn)是一個(gè)項(xiàng)目的根基,鑒權(quán)又是根基之本。先結(jié)合實(shí)際的項(xiàng)目背景來解釋什么是鑒權(quán):有選課系統(tǒng),會(huì)分成學(xué)生登陸,老師登陸,超級(jí)管理員登陸。那不同的人登陸的時(shí)候看到的菜單,內(nèi)容會(huì)不一樣。此時(shí)就不能只是鑒別賬號(hào)密碼是否正確,還要帶上登陸人相應(yīng)的操作權(quán)限。

當(dāng)然,鑒權(quán)可以不一定發(fā)生在登陸的時(shí)候,也可以發(fā)生在請求接口的時(shí)候。如果是只在接口層面鑒權(quán)的話,那菜單的展示只能不做鑒權(quán),也就是不同的賬號(hào)進(jìn)入看到的所有欄目都是完整的,只是沒有點(diǎn)擊和操作權(quán)限。在登陸的時(shí)候做鑒權(quán),就可以把菜單欄目整個(gè)都放在后端控制。

下面我就來談?wù)?,?xiàng)目中常用到的鑒權(quán)幾種形式:

鑒權(quán)的三種方式

一、采用springSecurity內(nèi)部的決策管理器和投票器實(shí)現(xiàn)

參考網(wǎng)址:
http://blog.51cto.com/5148737/2308131
https://blog.csdn.net/sinat_29899265/article/details/80771330
PS:個(gè)人覺得這種方式比較麻煩,不容易理解與配置,不推薦。

二、采用自行編寫Filter或者Interceptor去攔截

關(guān)于二者的書寫方法可以參考博主之前的博客:Fliter和Interceptor區(qū)別與@Autowired報(bào)錯(cuò)(空指針)解決,還是推薦采用springMVC的Interceptor,可以很便捷的對url進(jìn)行模糊的通配符**匹配攔截。

三、 利用Spring Security利用@PreAuthorize注解,去對類或者接口判斷是否具有訪問權(quán)限。

具體有兩種寫法:

    1. 一種是采用默認(rèn)提供的函數(shù)@PreAuthorize("hasAuthority('BBBB','AAAA'))。該方法要求在登陸鑒權(quán)那塊,需要將AAAA和BBBB權(quán)限給加入到springsecurity的User對象中。
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("BBBB"));
authorities.add(new SimpleGrantedAuthority("AAAA"));
//在創(chuàng)建user對象的時(shí)候要把a(bǔ)uthorities給注入
    Account account = new Account(id, username, "empty", true,
                    true, true, true, authorities);

其中Account繼承User類(springSecurity自帶的,實(shí)現(xiàn)了UserDetails接口的類),調(diào)用父類的初始化方法。

public Account(Long accountId, String username, String password, boolean enabled, boolean accountNonExpired,
            boolean credentialsNonExpired, boolean accountNonLocked,
            Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
        this.accountId = accountId;
    }

private final Set<GrantedAuthority> authorities; 是springsecurity里面user的成員變量。 這樣子user對象帶上了BBBB和AAAA的權(quán)限,然后再將整個(gè)user給注冊到springsecurity中:

public static void registerAuth(User account, ServletContext sc, HttpServletRequest request)
            throws AccessDeniedException {
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(account, "empty",
                account.getAuthorities());
        setDetails(request, auth);
        //重點(diǎn)在這句
        SecurityContextHolder.getContext().setAuthentication(auth);
        HttpSession session = request.getSession();
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                SecurityContextHolder.getContext());
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(sc);
        webApplicationContext.publishEvent(new InteractiveAuthenticationSuccessEvent(auth, SecurityUtils.class));

    }

那么就可以利用spring的上下文取出相應(yīng)的Account對象的權(quán)限:

public static Account getLoginAccount() {
        //重點(diǎn)在這句
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return null;
        }
        Object principal = authentication.getPrincipal();
        if (principal == null) {
            return null;
        }
        if (principal instanceof Account) {
            return (Account) principal;
        }
        return null;
    }
    1. 另外一種是采用自行撰寫函數(shù)去控制@PreAuthorize("@checkPhoneAuthorityUtil.check()")。該方法要求在相應(yīng)的類在spring容器之中,并且返回的是boolean類型。如果返回為false則無權(quán)限。
@Component("checkPhoneAuthorityUtil")
public class CheckPhoneAuthorityUtil {
    public Boolean check() {
        Account account = SecurityUtils.getLoginAccount();
        if (StringUtils.isBlank(account.getPhone())){
            throw new FantuanRuntimeException("請綁定手機(jī)號(hào)", HttpServletResponse.SC_UNAUTHORIZED);
        }
        return Boolean.TRUE;
    }
}
    1. 如果要多種方式去控制權(quán)限的話,只需要在兩種權(quán)限之間用and相連:
    @PostMapping(value = "/yktest")
    @PreAuthorize("hasAnyAuthority('AAAA','BBBB') and @checkPhoneAuthorityUtil .check()")
    @ResponseBody
    public RestResponse<String> yktest() throws Exception {
        //鑒權(quán)demo
        return RestResponse.ok("執(zhí)行完成");
    }

注:@PreAuthorize不僅可以注解在方法上,還可以注解在類上。

總結(jié)

雖然springSecurity給我們封裝了很好的一個(gè)接口,但是用起來比較笨重,并且靈活性感覺不夠,尤其是采用方法一的形式。并且如果在antMatchers()里面配置的話,就不會(huì)進(jìn)行鑒權(quán)了。所以當(dāng)需要多種鑒權(quán)等比較靈活的情況下,還是推薦自行撰寫攔截器,或者自行編寫的方法用在 @PreAuthorize中。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容