美文网首页
微服务 & SSO & Session & Token(Cook

微服务 & SSO & Session & Token(Cook

作者: 乌鲁木齐001号程序员 | 来源:发表于2020-07-21 12:06 被阅读0次

    三种架构

    前后端半分离架构
    前后端半分离架构.png
    前后端分离架构
    前后端分离架构.png
    将 Postman 升级成前端服务器
    将 Postman 升级成前端服务器.png

    前后端分离架构 | 实现概述

    • admin 项目的角色是:前端服务器;
    • admin 项目原本应该是个 Node.js + Angular 的项目,这里使用 Springboot + Angular 来代替;
    • Angular Build 完的结果,直接放在 Springboot 的 java/main/resource/static 下;
    • admin 项目向 zuul 发送请求;
    • 在认证服务器的客户端应用列表中,要加上 admin 项目;

    真实坏境下,Web 应用部署在 NodeJS 中,浏览器向 NodeJS 发请求,NodeJS 把请求发到 Zuul,Zuul 中完成限流、认证、审计、授权,完了调用业务系统;

    SSO & Authorizatin code grant & 前端服务器

    • OAuth2 的 Authorizatin code grant 认证模式的实现,需要引入前端服务器;
    • 引入了前端服务器,就实现了 SSO;
    • 当前端服务器重启后,浏览器访问前端服务器,直接就是登录状态;因为在 Authorizatin code grant 认证模式下,登录的位置是认证服务器,只要登录服务器上的 Session 没过期,认证服务器就知道,从浏览器来的请求是谁,不用再输用户名和密码了,然后直接跳到客户端应用,如果之前颁发给这个用户的 Token 没有过期,就把之前的 Token 返回给前端服务器;如果无效,就生成一个新 Token 发给前端服务器;这带来的一个问题是,如果用户的登出,只是清空前端服务器的 Session,会导致用户无法登出;
    • 前端服务器可以有多个,任何一个登录成功,Session 信息都会存在认证服务器中,当浏览器再发登录请求到第二个前端服务器中,前端服务器去认证服务器中拿的 Token 都是一样的,这就是 SSO;

    基于 Session 的 SSO

    2 个 Session
    • 认证服务器的 Session,存的是用户信息和 Session ID,Session ID 返回给浏览器作为 Cookie,这个 Cookie(SESSION) 和前端服务器返回给浏览器的 Cookie(JSESSIONID) 不是同一个;
    • 前端服务器的 Session,存的是用户的 Token;
    3 个有效期
    • 认证服务器 Session 的有效期,控制多长时间需要用户输一次用户名和密码;
    • 前端服务器 Session 的有效期,控制的是多长时间跳转一次认证服务器;
    • Token 的有效期,控制登录一次能访问多长时间的微服务;
    优点
    • Session 信息都是存储在服务器里,不论是前端服务器还是认证服务器;
    • Token 信息和 Session 信息都存储在数据库中,可控性高,可以看到系统中所有的登录状态,想让谁下线就让谁下线;
    • 没有跨域问题,客户端应用不管部署在什么域名下都可以和认证服务器交互,基于 Token 的 SSO 会有同域的问题;
    缺点
    • 复杂度高:2 套机制,Session + Token;
    • 性能比较低,占用应用服务器的内存存储 Session;
    适用
    • 适用与百万用户以下或有一定规模的公司的内部管理系统;

    Logout 时同步前端服务器 & 认证服务器的 Session

    • 点击 Logout 按钮,在前端服务器将 session 失效后,要向认证服务器发一个请求 http://auth.imooc.com:9090/logout?redirect_uri=http://admin.imooc.com:8080;
    • 重写 Spring Security 的 DefaultLogoutPageGeneratingFilter 过滤器,这个过滤器原本会生成一个是否确定登出的 Form 表单,重写后将其逻辑变成,Form 表单渲染好了之后,直接 Submit,并且通过一个 hidden 的 input 把参数 redirect_uri=http://admin.imooc.com:8080 提交给认证服务器的 Logout 逻辑;
    • 写一个 Logout 成功后要做什么的 Handler:OAuth2LogoutSuccessHandler,在 Logout 成功后,重定向到前端服务器的首页;
    • 把自己写的 OAuth2LogoutSuccessHandler 配置到认证服务器中:
    @Configuration
    @EnableWebSecurity // 让安全配置生效
    public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {   
    
        // ...
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin().and() // 这里可以配置自己的登录页
            .httpBasic().and()
            // 自己写的,logout 成功以后的 handler
            .logout().logoutSuccessHandler(logoutSuccessHandler)
            ;
        }
    
    }
    
    
    重要的事情说三遍
    • 跨域!跨域!跨域!
    • Logout 的 window.location.href = 'http://localhost:9090/logout?redirect_uri=http://localhost:8080' 和 Login 的 window.location.href = 'http://localhost:9090/oauth/authorize?' 的域名必须一样,否则,Logout 和 Login 带的 SESSION 会不一样,用户在数据库中的 session 信息删不掉,用户登出失败;

    Token 的有效期

    Token 是短活的令牌,Token 过期的时候,可能 Session 还没过期。

    refresh token
    refresh token.png
    • access_token:短生命周期;
    • refresh_token:长生命周期;
    • 客户端应用拿 refresh_token 不断的刷 access_token;
    access_token vs access_token
    • 拿到 access_token 可以任意访问微服务,而且是合法的;
    • refresh_token 在使用的时候,必须和 client_id,client_secret 一起使用;
    refresh_token 的启用
    • 字段 oauth_client_details.refresh_token_validity 必须要填,单位是 s,只要这个字段有值,在发 access_token 的同时,会发 refresh_token;
    • 去认证服务器刷新 Token 的客户端应用,在认证服务器中配置的信息的 authorized_grant_types 字段中,要加上 refresh_token
    • @EnableAuthorizationServer 的配置类中,要给 refresh_token 请求专门配置一个 userDetailsService:
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // 这个 userDetailsService 是专门给 refresh_token 用的
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore())
                // 这个是支持前 4 种认证模式的:password, code, ... , ...
                .authenticationManager(authenticationManager);
    }
    
    • refresh_token 请求的 grant_typerefresh_token

    refresh_token 过期了怎么办

    方案一 | 强制 Logout
    • 当 refresh_token 失败后,返回前端 500,完了带一个专门标记 refresh_token 失败的标记;
    • 前端的 HttpClient 加一个拦截器,在请求之后拦截,如果收到 refresh_token 失败的标记,调用 Logout 接口,前端服务器中的 session 失效,认证服务器中的 session 失效,让用户重新登录;
    方案二 | 让认证服务器决定
    • 直接请求认证服务器的 /oauth/authorize 接口,让认证服务器决定是否要重新登录;
    • 如果认证服务器中的 session 没过期,回调到前端服务器,前端服务器获取 Token 后存储在自己的 session 中;
    • 如果认证服务器中的 session 过期,让浏览器重定向到登录页面;

    基于 Token 的 SSO

    步骤
    • 浏览器访问首页,请求会先经过前端服务器的 CookieTokenFilter,发现请求既没有 access_token,也没有 refresh_token 后,会调用认证服务器的登录接口,认证服务器返回登录页,输入用户名密码后发请求到认证服务器登录;
    • 在认证服务器上登录成功后,重定向到前端服务器后,前端服务器获取到 Token 后,不存储在前端服务器本地的 session 中,而是将 Token(access_token, refresh_token) 作为 Cookie 返回给浏览器;
    • 浏览器重定向到首页,请求还是会经过 CookieTokenFilter,此时有 access_token,CookieTokenFilter 会把 access_token 放入 Header 中,然后放行到网关,请求进入网关限流拦截器,认证拦截器,审计拦截器,授权拦截器后进入 /user/me 拦截器,/user/me 拦截器判断请求的 Header 中有 username 信息(授权拦截器放进去的),返回 username 给前端,前端看到有返回数据,就进入登录后的页面;
    • 浏览器带着 Cookie(token) 访问 order 服务,经过 CookieTokenFilter 后,CookieTokenFilter 会把 token 放进 Header 中,经过网关的限流、认证、审计、授权后,会进入 order 服务,完成对 order 服务的访问;
    • 登出的时候,浏览器先把 Cookie 清空,然后访问认证服务器的登出接口,登出后,重定向到首页,首页会先访问 /user/me 接口,但是会被 CookieTokenFilter 拦截下来,CookieTokenFilter 发现没有 access_token 和 refresh_token,会访问认证服务器的登录接口,认证服务器返回登录页;
    基于 Token 的 SSO vs 基于 Session 的 SSO
    • 基于 Session 的 SSO,每当前端服务器的 session 失效后,就要访问认证服务器去判断 session 是否过期,由认证服务器决定放回前端服务器旧的 Token,还是返回浏览器登录页;
    • 基于 Token 的 SSO,当浏览器中的 Cookie 失效后,才会去认证服务器一次认证;
    • 不管那种方案,在认证服务器上,都会有用户的 session, 基于 Session 的 SSO 需要认证服务器上的 session 有效期较长,这样才不会导致前端服务器频繁的访问认证服务器;基于 Token 的 SSO 不需要认证服务器上的 session 有效期很长,只要浏览器中 Cookie 有效,就能访问服务,哪怕认证服务器中的 session 已经失效,只要 Cookie 中的 access_token 没失效,任然可以继续访问微服务;
    优点
    • 复杂度较低,只需要考虑两个 Token 过期要怎么处理就可以了;
    • 更适合海量用户的场景,比如有上千万的用户,不可能把这些用户的信息都存在 前端服务器的 session 中;
    缺点
    • 安全性较低,access_token 存储在 Cookie 中了,可以使用 HTTPS 保证 Cookie 的传递,还有就是放在 Cookie 中的 Token 不会有很长的有效期;如果是 JWT 的话,浏览器中还会存用户信息,那样的安全风险更高 ;
    • 可控性较低,因为 access_token 是存储在浏览器的 Cookie 中,没法由管理人员手动失效掉 access_token;
    • 没法跨域,因为给浏览器的 Cookie 是有域名限制的,不在同一个一级域名下的前端应用,是无法做到 SSO 的;可以通过往返回给浏览器的 Cookie 中加多个域名;

    相关文章

      网友评论

          本文标题:微服务 & SSO & Session & Token(Cook

          本文链接:https://www.haomeiwen.com/subject/adhdhktx.html