token处理之二使用JWT替换默认的token

JWT(Json Web Token) 特点:  

  1,自包含:jwt token包含有意义的信息

  spring security oauth默认生成的token是uuid,是无意义的,本身并不包含任何信息。这个token所包含的信息,如果用redis存储token ,会在redis里存储这些信息(数据库也一样):

  

  这样当用这个token去访问接口的时候,需要根据这个token 从redis中取出来存储的相关的信息,才能知道这个token所包含的信息。这中存储策略的特点是,如果redis或数据库服务挂了,这个token 就失效了,因为这个token本身是不包含信息的。

而JWT自包含的意思就是,JWT令牌本身是有信息的,拿到令牌后,解析令牌就能拿到包含的信息,不用去存储里取。

  2,密签

    发的令牌根据指定的秘钥签名(防止信息被篡改,不是加密)

  3,可扩展

    所包含的信息可以扩展

要将uuid换成JWT,需要做两件事,使用JWT ,有两个增强器:

1,第一个叫JwtAccessTokenConverter,作用是添加JWT签名,将uuid的token转为jwt,用秘钥签名

2,第二个叫 TokenEnhancer ,作用是往jwt里添加自定义的信息。由于默认生成uuid token的方法是private,所以通过ImoocJwtTokenEnhancer 往jwt里添加一些自定义的信息

最后在认证服务器ImoocAuthenticationServerConfig里,拿到增强器链TokenEnhancerChain,判断系统里这两个增强器是不是空,非空的话把这两个增强器连起来,加到TokenEndpoint 。

实现:

1,配置JwtAccessTokenConverter,新建一个配置类,先配置TokenStore 为JwtTokenStore ,然后JwtTokenStore 需要JwtAccessTokenConverter 这个转换器,在转换器里设置签名。

  1. /**
  2. * token存储到redis,默认是在内存不行
  3. * ClassName: TokenStoreConfig
  4. * @Description: token存储策略
  5. * @author lihaoyang
  6. * @date 2018年3月15日
  7. */
  8. @Configuration
  9. public class TokenStoreConfig {
  10.  
  11. @Autowired
  12. private RedisConnectionFactory redisConnectionFactory;
  13.  
  14. /**
  15. * 配置redis存储token
  16. * @Description: 配置文件有 imooc.security.oauth2.storeType = redis 时才生效
  17. * @param @return
  18. * @return TokenStore
  19. * @throws
  20. * @author lihaoyang
  21. * @date 2018年3月16日
  22. */
  23. @Bean
  24. @ConditionalOnProperty(prefix = "imooc.security.oauth2" , name = "storeType" , havingValue = "redis")
  25. public TokenStore redisTokenStore(){
  26. return new RedisTokenStore(redisConnectionFactory);
  27. }
  28.  
  29. /**
  30. * JWT配置
  31. * ClassName: JwtTokenConfig
  32. * @Description:\
  33. * @ConditionalOnProperty是说,有前缀imooc.security.oauth2.storeType = jwt 的配置时,这个类里的配置才生效
  34. * matchIfMissing =true 意思是当配置文件里不配置imooc.security.oauth2.storeType = jwt时,配置是生效的
  35. * @author lihaoyang
  36. * @date 2018年3月16日
  37. */
  38. @Configuration
  39. @ConditionalOnProperty(prefix = "imooc.security.oauth2" , name = "storeType" , havingValue = "jwt" , matchIfMissing = true)
  40. public static class JwtTokenConfig{
  41.  
  42. @Autowired
  43. private SecurityProperties securityProperties;
  44.  
  45. @Bean
  46. public TokenStore jwtTokenStore(){
  47. return new JwtTokenStore(jwtAccessTokenConverter());
  48. }
  49.  
  50. @Bean
  51. public JwtAccessTokenConverter jwtAccessTokenConverter(){
  52. JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
  53. jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJetSigningKey());//jwt签名
  54. return jwtAccessTokenConverter;
  55. }
  56. }
  57.  
  58. }

2,配置增强器 ImoocJwtTokenEnhancer往jwt增加自定义信息:

  1. /**
  2. * jwt增强器
  3. * ClassName: ImoocJwtTokenEnhancer
  4. * @Description:
  5. * 往jwt的 token增加自己的信息
  6. * spring默认生成token的方法在DefaultTokenService里,是private,生成的是uuid,没办法重写,只能是增强器把uuid转换成jwt,添加一些信息
  7. * @author lihaoyang
  8. * @date 2018年3月16日
  9. */
  10. public class ImoocJwtTokenEnhancer implements TokenEnhancer {
  11.  
  12. @Override
  13. public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
  14. //往jwt添加的自定义信息
  15. Map<String , Object> info = new HashMap<>();
  16. info.put("company", "imooc");
  17. info.put("product_code", "100");
  18. ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
  19. return accessToken;
  20. }
  21.  
  22. },

3,在认证服务器ImoocAuthenticationServerConfig判断是否存在2个增强器,并添加到TokenEndpoint (  /oauth/token处理的入口点)

  1. /**
  2. * 认证服务器
  3. * ClassName: ImoocAuthenticationServerConfig
  4. * @Description:
  5. * extends AuthorizationServerConfigurerAdapter 自定义token生成
  6. * @author lihaoyang
  7. * @date 2018年3月12日
  8. */
  9. @Configuration
  10. @EnableAuthorizationServer //这个注解就是实现了一个认证服务器
  11. public class ImoocAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter{
  12.  
  13. /*
  14. * 不继承AuthorizationServerConfigurerAdapter,这些bean会自己找,配了,就要自己实现
  15. */
  16.  
  17. @Autowired
  18. private AuthenticationManager authenticationManager;
  19.  
  20. @Autowired
  21. private UserDetailsService userDetailsService;
  22.  
  23. //配置文件
  24. @Autowired
  25. private SecurityProperties securityProperties;
  26.  
  27. //token存在redis,默认是在内存
  28. @Autowired
  29. private TokenStore tokenStore;
  30.  
  31. /**
  32. * jwt需要的两个增强器之一:将uuid转换为jwt
  33. * 有jwt配置时才生效
  34. */
  35. @Autowired(required = false)
  36. private JwtAccessTokenConverter jwtAccessTokenConverter;
  37.  
  38. /**
  39. * jwt需要的两个增强器之二:往jwt添加自定义信息
  40. */
  41. @Autowired(required = false)
  42. private TokenEnhancer jwtTokenEnhancer;
  43.  
  44. /**
  45. * 配置TokenEndpoint 是 /oauth/token处理的入口点
  46. */
  47. @Override
  48. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  49. endpoints.tokenStore(tokenStore)
  50. .authenticationManager(authenticationManager)
  51. .userDetailsService(userDetailsService);
  52.  
  53. /**
  54. * 使用JWT ,有两个增强器:
  55. * 1,使用JwtAccessTokenConverter将uuid的token转为jwt,用秘钥签名
  56. * 2,由于默认生成uuid token的方法是private,所以通过ImoocJwtTokenEnhancer 往jwt里添加一些自定义的信息
  57. *
  58. * 在这里拿到增强器的链,把这两个增强器连起来
  59. */
  60. if(jwtAccessTokenConverter != null && jwtTokenEnhancer != null){
  61. //拿到增强器链
  62. TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
  63.  
  64. List<TokenEnhancer> enhancers = new ArrayList<TokenEnhancer>();
  65. enhancers.add(jwtAccessTokenConverter);
  66. enhancers.add(jwtTokenEnhancer);
  67.  
  68. enhancerChain.setTokenEnhancers(enhancers);
  69.  
  70. endpoints.tokenEnhancer(enhancerChain)
  71. .accessTokenConverter(jwtAccessTokenConverter);
  72. }
  73. }
  74.  
  75. /**
  76. * 功能:认证服务器会给哪些第三方应用发令牌
  77. * 覆盖了该方法,application.properties里配置的
  78. * security.oauth2.client.clientId = imooc
  79. * security.oauth2.client.clientSecret = imoocsecret
  80. * 就失效了
  81. */
  82. @Override
  83. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  84. //1,写死
  85. // clients.jdbc(dataSource)就是qq场景用的,有第三方公司注册过来,目前场景是给自己的应用提供接口,所以用内存就行
  86. // clients.inMemory()
  87. // //~========================== 在这里配置和写配置文件一样================
  88. // .withClient("imooc") //第三方应用用户名
  89. // .secret("imoocsecret") //密码
  90. // .accessTokenValiditySeconds(7200)//token有效期
  91. // .authorizedGrantTypes("password","refresh_token") //支持的授权模式
  92. // .scopes("all","read","write") //相当于oauth的权限,这里配置了,请求里的必须和这里匹配
  93. // //~=======如果有多个client,这里继续配置
  94. // .and()
  95. // .withClient("xxxxx");
  96.  
  97. //2,读取配置文件
  98. InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
  99. //判断是否配置了客户端
  100. if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())){
  101. for (OAuth2ClientProperties config : securityProperties.getOauth2().getClients()) {
  102. builder.withClient(config.getClientId())
  103. .secret(config.getClientSecret())
  104. .accessTokenValiditySeconds(config.getAccessTokenValiditySeconds())
  105. .authorizedGrantTypes("password","refresh_token") //这些也可以配置也可以写死,看心情
  106. .scopes("all","read","write");
  107. }
  108. }
  109.  
  110. }
  111. }

重新获取token:

  1. {
  2. "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MjExNjYwNDksInVzZXJfbmFtZSI6IjEzODEyMzQ5ODc2IiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiYzRkYWQzYTMtM2I0Ni00N2FlLTgzYzAtYzkyNjg2MzU5ZjI0IiwiY2xpZW50X2lkIjoiaW1vb2MiLCJzY29wZSI6WyJhbGwiLCJyZWFkIiwid3JpdGUiXX0.R6-l64ogfHGRACNLiz3_-d-KT8AnN0jmSYzplpkSy-0",
  3. "token_type": "bearer",
  4. "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiIxMzgxMjM0OTg3NiIsInNjb3BlIjpbImFsbCIsInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiJjNGRhZDNhMy0zYjQ2LTQ3YWUtODNjMC1jOTI2ODYzNTlmMjQiLCJleHAiOjE1MjM3NTc5ODksImF1dGhvcml0aWVzIjpbImFkbWluIiwiUk9MRV9VU0VSIl0sImp0aSI6ImQ3N2Y3ZjVkLTE1OTctNGI5My1hNzU5LWUyYWVlYTBjM2UxMSIsImNsaWVudF9pZCI6Imltb29jIn0.ZKMUKmprgtFbsWBAFAI_BKsBVQ9RUGdbhViG3OyyIJU",
  5. "expires_in": 59,
  6. "scope": "all read write",
  7. "jti": "c4dad3a3-3b46-47ae-83c0-c92686359f24"
  8. }

此时redis中就没有token信息了

访问https://www.jsonwebtoken.io/ 可以解析jwt

至此已完成JWT配置,但是如果想在controller 获取到往JWT里自定义的信息,还需要添加一些配置

在demo项目(应用的项目)添加maven依赖:

  1. <!-- 解析jwt -->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt</artifactId>
  5. <version>0.7.0</version>
  6. </dependency>

在controller里:

  1. @GetMapping("/me2")
  2. public Object getCurrentUser2(Authentication user,HttpServletRequest request) throws Exception{
  3.  
  4. String header = request.getHeader("Authorization");
  5. String token = StringUtils.substringAfter(header, "bearer ");
  6.  
  7. Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("UTF-8")).parseClaimsJws(token).getBody();
  8. String company = (String) claims.get("company");
  9. String productId = (String) claims.get("product_id");
  10. System.err.println("token decode ------>company:"+company+",productId:"+productId);
  11. return user;
  12. }

但是我一直打印不了,不知道为什么?谁知道告诉我

Token刷新:

token是有有效期的,当token过期后,响应信息会提示token过期,又得重新登录才能访问接口, 有个token的刷新机制,我们请求 token的时候,会返回一个 refresh_token ,这个就是在token过期后,可以拿着它去换取一个新的token,这个过程应该是在用户无感知的情况下进行的。实验表明,对于没有过期的token也可以刷新,会返回一个新的token,但是之前的还可以用(这样做没有什么意义)

刷新token访问的url还是 http://127.0.0.1:8080/oauth/token

需要的参数

具体代码放在了github:https://github.com/lhy1234/spring-security

Spring Security构建Rest服务-1205-Spring Security OAuth开发APP认证框架之Token处理的更多相关文章

  1. Spring Security构建Rest服务-1204-Spring Security OAuth开发APP认证框架之Token处理

    token处理之一基本参数配置 处理token时间.存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframe ...

  2. Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录

    基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...

  3. Spring Security构建Rest服务-1201-Spring Security OAuth开发APP认证框架之实现服务提供商

    实现服务提供商,就是要实现认证服务器.资源服务器. 现在做的都是app的东西,所以在app项目写代码  认证服务器: 新建 ImoocAuthenticationServerConfig 类,@Ena ...

  4. Spring Security构建Rest服务-1200-SpringSecurity OAuth开发APP认证框架

    基于服务器Session的认证方式: 前边说的用户名密码登录.短信登录.第三方登录,都是普通的登录,是基于服务器Session保存用户信息的登录方式.登录信息都是存在服务器的session(服务器的一 ...

  5. Spring Security构建Rest服务-1202-Spring Security OAuth开发APP认证框架之重构3种登录方式

    SpringSecurityOAuth核心源码解析 蓝色表示接口,绿色表示类 1,TokenEndpoint 整个入口点,相当于一个controller,不同的授权模式获取token的地址都是 /oa ...

  6. Spring Security构建Rest服务-1203-Spring Security OAuth开发APP认证框架之短信验证码登录

    浏览器模式下验证码存储策略 浏览器模式下,生成的短信验证码或者图形验证码是存在session里的,用户接收到验证码后携带过来做校验. APP模式下验证码存储策略 在app场景下里是没有cookie信息 ...

  7. Spring Cloud构建微服务架构(三)消息总线

     注:此文不适合0基础学习者直接阅读,请先完整的将作者关于微服务的博文全部阅读一遍,如果还有疑问,可以再来阅读此文,地址:http://blog.csdn.net/sosfnima/article/d ...

  8. 构建微服务:Spring boot

    构建微服务:Spring boot 在上篇文章构建微服务:Spring boot 提高篇中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jp ...

  9. Spring Cloud构建微服务架构(二)服务消费者

    Netflix Ribbon is an Inter Process Communication (IPC) cloud library. Ribbon primarily provides clie ...

随机推荐

  1. python cov()

    在PCA中涉及到了方差var和协方差cov,下面详细了解这两个函数的用法.numpy中var和cov函数求法和MATLAB中var和cov函数求法类似. 首先均值,样本方差,样本协方差公式分别为 其中 ...

  2. python socket.error: [Errno 10061]

    用Python写server和client时候如果server中sock.bind(('localhost', 8001))  则client中sock.connect(('localhost', 8 ...

  3. 1028. Hanoi Tower Sequence

    1028. Hanoi Tower Sequence Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description Hanoi Tow ...

  4. 转:getContextPath、getServletPath、getRequestURI的区别

    假定你的web application 名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下结果 ...

  5. 论文笔记(3)-Extracting and Composing Robust Features with Denoising Autoencoders

    这篇文章是Bengio研究的在传统的autoencoder基础上增加了噪声参数,也就是说在输入X的时候,并不直接用X的数据,而是按照一定的概率来清空输入为0.paper中的名词为corrupted.这 ...

  6. IIS配置404页面配置,IIS自定义404页面

    .NET 环境下 用到404页的场景一般有两种: 场景一:报黄页,程序性的错误,代码层可以捕捉到的. 场景二:用户输入不存在的页面,代码层捕捉不到的. IIS 默认会有404的配置,不过这种呈现出的都 ...

  7. 常用到的一些js方法,记录一下

    获取字符串长度 function GetStringLength(str) { return str.replace(/[^\x00-\xff]/g, "00").length; ...

  8. .net core 与ELK(4)后台运行els可视化工具和Kibana

    which nohup .bash_profile中并source加载 如果没有就安装吧 yum provides */nohup nohup npm run start & nohup ./ ...

  9. Enum 绑定到 CheckBox

    第一种方法: 后台: internal static class EnumCache<T> where T : struct, IConvertible { private static ...

  10. 2D Convex Hulls and Extreme Points( Convex Hull Algorithms) CGAL 4.13 -User Manual

    1 Introduction A subset S⊆R2 is convex if for any two points p and q in the set the line segment wit ...