shiro攔截認證的全過程記錄

概述

Shiro是apache旗下一個開源安全框架(http://shiro.apache.org/),它將軟件系統的安全認證相關的功能抽取出來,實現用戶身份認證,權限授權、加密、會話管理等功能,組成瞭一個通用的安全認證框架。使用shiro就可以非常快速的完成認證、授權等功能的開發,降低系統成本。

Shiro框架三大核心對象

說明:

1)Subject :主體對象,負責提交用戶認證和授權信息。

2)SecurityManager:安全管理器,負責認證,授權等業務實現。(核心)

3)Realm:領域對象,負責從數據層獲取業務數據。

shrio 攔截認證全過程

 1.FilterRegistrationBean過濾註冊bean

@Bean
public FilterRegistrationBean shiroFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
    //該值缺省為false,表示生命周期由SpringApplicationContext管理,設置為true則表示由ServletContainer管理
    registration.addInitParameter("targetFilterLifecycle", "true");
    registration.setEnabled(true);
    registration.setOrder(Integer.MAX_VALUE - 1);
    registration.addUrlPatterns("/*");
    return registration;
}

設置過濾的bean

2.shiroFilter 實際過濾配置bean

@Bean("shiroFilter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
    shiroFilter.setSecurityManager(securityManager);

    //oauth過濾
    Map<String, Filter> filters = new HashMap<>(10);
    filters.put("oauth2", new Oauth2Filter());
    shiroFilter.setFilters(filters);

    Map<String, String> filterMap = new LinkedHashMap<>();
    filterMap.put("/webjars/**", "anon");
    filterMap.put("/druid/**", "anon");
    filterMap.put("/login", "anon");

    filterMap.put("/**", "oauth2");
    shiroFilter.setFilterChainDefinitionMap(filterMap);

    return shiroFilter;
}

配置oauth2Filter為過濾類 過濾對象處/webjars/** /druid/** /login 外的所有

3.過濾類Oauth2Filter 繼承 AuthenTicationFilter 重寫以下方法

/**
 * 驗證是否有效token
 * @param request re
 * @param response res
 * @return 驗證token
 * @throws Exception
 */
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    //獲取請求token,如果token不存在,直接返回401
    String token = getRequestToken((HttpServletRequest) request);
    if(StringUtils.isBlank(token)){
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());

        String json = new Gson().toJson(new Result().error(ErrorCode.UNAUTHORIZED));

        httpResponse.getWriter().print(json);

        return false;
    }

    return executeLogin(request, response);
}

4.調用父類 executeLogin 進行登錄驗證

protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
    AuthenticationToken token = this.createToken(request, response);
    if (token == null) {
        String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
        throw new IllegalStateException(msg);
    } else {
        try {
            Subject subject = this.getSubject(request, response);
            subject.login(token);
            return this.onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException var5) {
            return this.onLoginFailure(token, var5, request, response);
        }
    }
}

5.subject.login(token); 進行登錄

login方法被DelegatingSubject重寫

public void login(AuthenticationToken token) throws AuthenticationException {
    **
    Subject subject = this.securityManager.login(this, token);
    **
}

6.securityManager.login(this, token) login被DefaultSecurityManager

接下來幾步沒那麼重要省略部分

7.ModularRealmAuthenticator AuthenticationInfo 授權信息獲取方法

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    this.assertRealmsConfigured();
    Collection<Realm> realms = this.getRealms();
    return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}

getRealms 獲取我們自己重寫的Realms類,主要用戶獲取用戶信息

8.接下來則進入我們自己寫的Realms類 我的類叫Oauth2Realm

/**
 * 認證(登錄時調用)
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String accessToken = (String) token.getPrincipal();

    //根據accessToken,查詢用戶信息
    SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken);
    //token失效
    if(tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){
        throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
    }

    //查詢用戶信息
    SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());

    //轉換成UserDetail對象
    UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);

    //獲取用戶對應的部門數據權限
    List<Long> deptIdList = shiroService.getDataScopeList(userDetail.getId());
    userDetail.setDeptIdList(deptIdList);

    //賬號鎖定
    if(userDetail.getStatus() == 0){
        throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK));
    }

    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName());
    return info;
}

負責獲取用戶信息的方法

這並不是登錄的過程,而是授權過濾的過程,通過token到數據庫查詢是否有這個用戶,且沒有過期,則證明已經登錄。

總結

到此這篇關於shrio攔截認證的文章就介紹到這瞭,更多相關shrio攔截認證內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: