zoukankan      html  css  js  c++  java
  • 认证和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

  • 相关阅读:
    分享:CodeLite 5.1 发布,C++ 集成开发环境
    Elementary Sockets:readn, writen, and readline Functions
    How to handle boost::iostreams::gzip_dec C++ Forum
    分享:python/c++ 深拷贝与浅拷贝(转)写∽好
    我刚刚通过 @盛大网盘EverBox 分享了 Web协议与实践HTTP.、网络协议、缓存技术和....pdf, 欢迎大家下载!
    Is zlib threadsafe?
    print "\v"
    分享:混合编译c和cpp的makefile模板
    Elementary Sockets:readn, writen, and readline Functions
    c的socket教程 二进制 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/caofanqi/p/12274387.html
Copyright © 2011-2022 走看看