認證和SSO(四)-基於session的SSO存在的問題之token問題及基於session的SSO優缺點


1、access_token的有效期

  我們知道 token的有效期,是控制登陸一次能訪問多長時間微服務。那么access_token一般設置多長時間比較好呢?因為有access_token可以直接訪問微服務,而不需要再次認證,所以access_token不建議設置很長的有效期,一般為一到兩個小時,因為access_token一旦泄漏,風險很高。但是這樣又會有一個問題,access_token過期了,而session沒有過期,這樣會導致,我們還是登陸的狀態,但是無法訪問微服務了。

  將access_toen過期時間設置為10s,后訪問如下

2、OAuth2的refresh_token

  為了解決access_token過期的問題,OAuth2協議為我們提供了refresh_token。我們在配置支持refresh_token后,在通過密碼模式和授權碼模式獲取access_token的同時,也會發給我們一個refresh_token。在當access_token過期后,客戶端應用可以拿着refresh_token和clientId、clientSecret去授權服務器獲取新的access_token,在這個過程中不需要用戶再次輸入用戶名和密碼。因為refresh_token要配合clientId、clientSecret一起使用才有效,所以就算泄漏了refresh_token也不要緊,保護好clientSecret就好了。因此refresh_token可以設置較長的時間,一般與認證服務器session過期時間設置一致即可。

3、配置項目支持refresh_token

3.1、配置授權服務器支持refresh_token

  3.1.1、數據庫配置authorized_grant_types添加refresh_token,refresh_token_validity設置refresh_token過期時間,在使用密碼或授權碼模式申請令牌是refresh_token會一起返回

  配置完成后,通過http請求工具,測試獲取令牌中已經存在refresh_token

  3.1.2、OAuth2認證服務器配置類的public void configure(AuthorizationServerEndpointsConfigurer endpoints)要配置userDetailsService,因為刷新令牌沒有密碼只有用戶名。

    /**
     * 配置授權服務器終端的非安全特征
     * authenticationManager 校驗用戶信息是否合法
     * tokenStore:token存儲
     * userDetailsService:配合刷新令牌使用
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).tokenStore(new JdbcTokenStore(dataSource))
        .userDetailsService(userDetailsService);
    }

3.2、客戶端應用改造access_token過期時,可以刷新令牌

  SessionTokenFilter中獲取出token是要判斷access_token是否過期,過期執行刷新令牌操作

/**
 * 將session中的token取出放到請求頭中
 *
 * @author caofanqi
 * @date 2020/2/6 0:34
 */
@Slf4j
@Component
public class SessionTokenFilter extends ZuulFilter {

    private RestTemplate restTemplate = new RestTemplate();

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        TokenInfoDTO token = (TokenInfoDTO) request.getSession().getAttribute("token");

        if (token != null) {
            String accessToken = token.getAccess_token();
            //判斷access_token是否過期,需要刷新令牌
            if (token.isExpires()){
                String oauthTokenUrl = "http://gateway.caofanqi.cn:9010/token/oauth/token";

                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                headers.setBasicAuth("webApp", "123456");

                MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
                params.set("grant_type", "refresh_token");
                params.set("refresh_token", token.getRefresh_token());

                HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);

                ResponseEntity<TokenInfoDTO> refreshTokenResult = restTemplate.exchange(oauthTokenUrl, HttpMethod.POST, httpEntity, TokenInfoDTO.class);
                request.getSession().setAttribute("token", refreshTokenResult.getBody().init());
                accessToken = refreshTokenResult.getBody().getAccess_token();
                log.info("refresh_token......");
            }

            requestContext.addZuulRequestHeader("Authorization","bearer " + accessToken);
        }

        return null;
    }
}

  啟動個項目,測試

4、refresh_token過期了怎么辦

  雖然refresh_token的有效期比較長,但還是會過期的。如果refresh_token也過期了怎么處理呢?下面提供兩種方案,具體使用那種,根據需求來決定。

4.1、refresh_token過期了,不管session過沒過期,強制退出,從新輸用戶名密碼進行認證。

  4.1.1、修改SessionTokenFilter,刷新token失敗后,返回錯誤信息供頁面處理

  4.1.2、修改index.html,獲取異常,如果是token刷新失敗,直接退出登陸

    //攔截全局錯誤響應 ,使用方法https://www.runoob.com/jquery/ajax-ajaxerror.html
    $(document).ajaxError(function(event, jqXHR, options, errorMsg) {
        //jqXHR打印如下 alert(JSON.stringify(jqXHR));
        //{"readyState":4,"responseText":"{\"message\":\"refresh fail\"}","responseJSON":{"message":"token refresh fail"},"status":500,"statusText":"error"}
        if (jqXHR.status == 500 && jqXHR.responseJSON.message == 'token refresh fail') {
            alert("登陸信息以失效");
            //方案1、退出,重新登陸,走認證流程
            logout();

    });

4.2、refresh_token過期了,到認證服務器重新認證,如果session沒過期,直接返回一個token回來;如果session過期了,重新輸入用戶名密碼認證。

    //攔截全局錯誤響應 ,使用方法https://www.runoob.com/jquery/ajax-ajaxerror.html
    $(document).ajaxError(function(event, jqXHR, options, errorMsg) {
        //jqXHR打印如下 alert(JSON.stringify(jqXHR));
        //{"readyState":4,"responseText":"{\"message\":\"refresh fail\"}","responseJSON":{"message":"token refresh fail"},"status":500,"statusText":"error"}
        if (jqXHR.status == 500 && jqXHR.responseJSON.message == 'token refresh fail') {
            alert("登陸信息以失效");
            //方案1、退出,重新登陸,走認證流程
            //logout();

            //方案2、去認證服務器認證
            location.href = "http://auth.caofanqi.cn:9020/oauth/authorize?" +
                "client_id=webApp&" +
                "redirect_uri=http://web.caofanqi.cn:9000/oauth/callback&" +
                "response_type=code&" +
                "state=abc";
        }
    });

5、基於session的SSO的優缺點及適用場景

5.1、優點

  安全:所有的token信息都是存放在session中,在瀏覽器里只有一個JSESSIONID,只要做好防session固定攻擊,一般不會有什么風險。

  可控性高:token和session存在了數據庫,可控性較高。

  跨域:客戶端應用部署在哪個域名下,都可以直接跟認證服務器交互。

5.2、缺點

  復雜度高:各種過期時間較多,兩個session(客戶端和認證服務器),兩個token(access_token、refresh_token),必須請求每個東西是干什么的,過期后對系統會產生什么樣的行為,怎么進行處理,都要清楚。

  性能低:session、token的存取都會占用服務器資源,用戶量很大的情況下,會產生各種問題。

5.3、適用場景

  系統用戶百萬以下(token表的性能可以得到保證)。

  多個開發團隊開發多個客戶端,每個客戶端單獨部署並且頁面風格相同(各個系統可以跳來跳去,后面是一個統一的認證服務器,通過網關來訪問微服務)。

 

 

項目源碼:https://github.com/caofanqi/study-security/tree/dev-web-sso-session2

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM