Spring Boot+Spring Security+Spring Social项目开发(七):使用JWT替换默认Token、JWT实现SSO单点登录

github已经上传:https://github.com/13652493839/TiHom-Security

各位如果可以就给我star哈哈谢谢啦


Spring Boot+Spring Security+Spring Social项目开发(九):Spring Security授权表达式、重构配置方面的内容、数据库RBAC数据模型控制权限
Spring Boot+Spring Security+Spring Social项目开发(八):Spring Security 控制授权、源码解析
Spring Boot+Spring Security+Spring Social项目开发(六):开发APP认证框架、Spring Security OAuth核心源码、重构三种登录方式、重构社交登录
Spring Boot+Spring Security+Spring Social项目开发(五):微信开发、绑定与解绑、Session管理、退出登录
Spring Boot+Spring Security+Spring Social项目开发(四):使用Spring Social开发第三方登录、QQ登录开发
Spring Boot+Spring Security项目开发(三):实现短信验证码登录
Spring Boot+Spring Security项目开发(二):Spring Security、实现图形验证码功能、实现”记住我”功能
Spring Boot+Spring Security项目开发(一):RESTful API介绍


JWT和SSO


Token处理

  • 基本的Token参数配置(代码中的解释很详细)
    • 配置认证服务器TiHomAuthorizationServerConfig
    • core中创建类OAuth2Properties和OAuth2ClientProperties,OAuth2Properties用来管理OAuth2ClientProperties对象,也就是多个第三方应用的client管理;OAuth2ClientProperties是对单个client属性的配置
    • 把OAuth2Properties添加到SecurityProperties中
    • 去application.properties中配置

      tihom.security.oauth2.clients[0].clientId=tihom
      tihom.security.oauth2.clients[0].clientSecret=tihomsecret
      tihom.security.oauth2.clients[0].accessTokenValiditySeconds=3600
      tihom.security.oauth2.clients[1].clientId=test
      tihom.security.oauth2.clients[1].clientSecret=test
      #tihom.security.oauth2.clients[1].accessTokenValiditySeconds=(默认为0)
    • 把securityProperties注入到我们的认证服务器里面去
    • 配置令牌的存储:使用redis,令牌访问频繁,而且不需要去维护数据库表,性能比数据库好
    • 定义一个TokenStoreConfig类,这个类的作用就是从连接工厂拿到redis的连接,然后连到redis服务器上,如果产生token,就会存到指定的redis中
  • 使用JWT替换默认的Token
    • JWT(Json Web Token)
      • 1.自包含
        原本的token形式依赖存储,如果存储挂掉了令牌就毫无用处了,因为token本身是没有信息的;而JWT则本身带有信息,拿到令牌后直接解析令牌包含的信息,而不用去存储里面读信息.
      • 2.密签
        发出去的令牌可以使用指定的秘钥进行签名,签名不等于加密,只是防止别人去篡改信息,别人篡改我们可以知道,是一个开放的标准,对外界透明的,所以不要把敏感信息放在JWT中
      • 3.可扩展
    • 开发流程(代码中解释很详细)
      • 在TokenStoreConfig中添加一个静态类JwtTokenConfig
      • 将**写入OAuth2Properties中
      • 在认证服务器中加入

        @Autowired(required = false)
        private JwtAccessTokenConverter jwtAccessTokenConverter;

        并且在configure中做判断
      • 将demo中的UserController中”/me”的传入参数改为(Authentication user)
      • 在TokenStoreConfig的静态类JwtTokenConfig中添加

        /**
        .* token增强器
        */
        @Bean
        @ConditionalOnMissingBean(name = "jwtTokenEnhancer")
        //在不同系统里面可能处理是不同的,这里只是提供默认的,系统可以通过自己加入一个叫jwtTokenEnhancer的bean覆盖掉这个默认的逻辑
        public TokenEnhancer jwtTokenEnhancer(){
        return new TiHomJwtTokenEnhancer();
        }
      • 写TokenEnhancer增强器的实现方法TiHomJwtTokenEnhancer
      • 在认证服务器中加上

        @Autowired(required = false)
        private TokenEnhancer jwtTokenEnhancer;

        同时在configure中的判断做处理
      • (Authentication user)不知道你的自定义信息是什么的,如果要知道的话就需要自己写代码实现,先加依赖,在demo的pom中加

        <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
        </dependency>

        将(Authentication user)改为(Authentication user,HttpServletRequest request)

        @GetMapping("/me")
        public Object getCurrentUser(Authentication user/* Authentication authentication*/,HttpServletRequest request)
        throws Exception {
        //获取请求头里面Authentication的值
        String header = request.getHeader("Authentication");
        String token = StringUtils.substringAfter(header,"bearer ");
        //JWT的解析器,把jwtToken转化成了claims对象
        Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("UTF-8"))
        .parseClaimsJws(token).getBody();
        String company = (String) claims.get("company");
        //要用日志工具,不要用sout
        System.out.println("-->"+company);
        return user;
        }
    • 令牌的刷新
      • 令牌的时间在设置的失效时间后access_token就会失效了
      • 令牌失效后当我们再去获取令牌的时候同时发一个refresh_token给它,refresh_token在用户无感知的情况下去刷新令牌,得到一个新的access_token
      • refreshToken的失效时间在认证服务器中配置一下
  • 扩展和解析JWT的信息

JWT实现SSO单点登录

比如淘宝和天猫,他们是在不同的服务器上的,我们在淘宝点击登录,跳转到了login.taobao.com,这是一台新的服务器 ,也就是淘宝前台、天猫前台、淘宝登录是三个不同的服务器,而淘宝登录成功之后,在淘宝前台和天猫前台都是已登录状态

  • 基于JWT实现SSO
    Spring Boot+Spring Security+Spring Social项目开发(七):使用JWT替换默认Token、JWT实现SSO单点登录

  • 认证服务器

    • 另外创一个项目sso-demo,子项目sso-client1、sso-client2、sso-server,配置好pom即可.
    • 在server中建SSOServerApplication类,用来启动SpringBoot
    • 在application.properties做相应的配置
    • 在sever包下声明认证服务器SSOAuthorizationServerConfig,配置JWT和服务器的安全配置,重写三个configure方法
  • Client服务器
    • pom跟server的一模一样
    • 也是建SSOClient1Application类,用来启动SpringBoot同时作为controller,加上@EnableOAuth2Sso注解,让sso生效
    • 在application.properties做相应的配置
    • 建index.html
    • 改造登录体验,每次访问主页都要弹窗,我们把它改造成表单登录方式, 只需建SSOSecurityConfig类(声明是Spring配置类)覆盖掉其中一个带HttpSecurity参数的configure方法改为表单登录,任何请求都要身份认证

      http.formLogin().and().authorizeRequests().anyRequest().authenticated();

      不使用application.properties配置用户名信息和密码的形式来认证,而采用读数据库中的信息的方式来做认证,建SSOUserDetailsService类,声明是Spring的组件,里面重写loadUserByUsername方法,按用户名加载用户,同时进行密码加密,并且SSOSecurityConfig类中重写带AuthenticationManagerBuilder的configure方法覆盖原方法的配置,告诉它用我自己的userDetailsService和passwordEncoder来进身份验证
    • 改造授权逻辑,不要显示授权页面让我们选择Authenticate还是deny,直接让它默认授权
      • 找到这段代码的根源在哪里,发现是在WhitelabelApprovalEndpoint类中,我们需要将这整个类的代码拷贝出来自己写一个类采用拷贝进来的代码进行部分修改达到效果,原类的声明是@FrameworkEndpoint注解(作用与@RestController相似),如果请求的url一样的话,我们声明@RestController会优先使用我们的rest处理
      • 实际上跳转到授权页面的时候现在会到我们定义的url里面,二它是把TEMPLATE这个页面展示出来的,我们只需要在这里进行修改即可,这里demo项目就简单的把这个页面的内容屏蔽掉然后直接写js脚本提交后跳转,如果要更好的效果自己慢慢细化修改,但是在我实测时发现基本连闪一下都没有哈哈.