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 这个转换器,在转换器里设置签名。

/**
* token存储到redis,默认是在内存不行
* ClassName: TokenStoreConfig
* @Description: token存储策略
* @author lihaoyang
* @date 2018年3月15日
*/
@Configuration
public class TokenStoreConfig { @Autowired
private RedisConnectionFactory redisConnectionFactory; /**
* 配置redis存储token
* @Description: 配置文件有 imooc.security.oauth2.storeType = redis 时才生效
* @param @return
* @return TokenStore
* @throws
* @author lihaoyang
* @date 2018年3月16日
*/
@Bean
@ConditionalOnProperty(prefix = "imooc.security.oauth2" , name = "storeType" , havingValue = "redis")
public TokenStore redisTokenStore(){
return new RedisTokenStore(redisConnectionFactory);
} /**
* JWT配置
* ClassName: JwtTokenConfig
* @Description:\
* @ConditionalOnProperty是说,有前缀imooc.security.oauth2.storeType = jwt 的配置时,这个类里的配置才生效
* matchIfMissing =true 意思是当配置文件里不配置imooc.security.oauth2.storeType = jwt时,配置是生效的
* @author lihaoyang
* @date 2018年3月16日
*/
@Configuration
@ConditionalOnProperty(prefix = "imooc.security.oauth2" , name = "storeType" , havingValue = "jwt" , matchIfMissing = true)
public static class JwtTokenConfig{ @Autowired
private SecurityProperties securityProperties; @Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
} @Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJetSigningKey());//jwt签名
return jwtAccessTokenConverter;
}
} }

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

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

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

/**
* 认证服务器
* ClassName: ImoocAuthenticationServerConfig
* @Description:
* extends AuthorizationServerConfigurerAdapter 自定义token生成
* @author lihaoyang
* @date 2018年3月12日
*/
@Configuration
@EnableAuthorizationServer //这个注解就是实现了一个认证服务器
public class ImoocAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter{ /*
* 不继承AuthorizationServerConfigurerAdapter,这些bean会自己找,配了,就要自己实现
*/ @Autowired
private AuthenticationManager authenticationManager; @Autowired
private UserDetailsService userDetailsService; //配置文件
@Autowired
private SecurityProperties securityProperties; //token存在redis,默认是在内存
@Autowired
private TokenStore tokenStore; /**
* jwt需要的两个增强器之一:将uuid转换为jwt
* 有jwt配置时才生效
*/
@Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter; /**
* jwt需要的两个增强器之二:往jwt添加自定义信息
*/
@Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer; /**
* 配置TokenEndpoint 是 /oauth/token处理的入口点
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService); /**
* 使用JWT ,有两个增强器:
* 1,使用JwtAccessTokenConverter将uuid的token转为jwt,用秘钥签名
* 2,由于默认生成uuid token的方法是private,所以通过ImoocJwtTokenEnhancer 往jwt里添加一些自定义的信息
*
* 在这里拿到增强器的链,把这两个增强器连起来
*/
if(jwtAccessTokenConverter != null && jwtTokenEnhancer != null){
//拿到增强器链
TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancers = new ArrayList<TokenEnhancer>();
enhancers.add(jwtAccessTokenConverter);
enhancers.add(jwtTokenEnhancer); enhancerChain.setTokenEnhancers(enhancers); endpoints.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
} /**
* 功能:认证服务器会给哪些第三方应用发令牌
* 覆盖了该方法,application.properties里配置的
* security.oauth2.client.clientId = imooc
* security.oauth2.client.clientSecret = imoocsecret
* 就失效了
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//1,写死
// clients.jdbc(dataSource)就是qq场景用的,有第三方公司注册过来,目前场景是给自己的应用提供接口,所以用内存就行
// clients.inMemory()
// //~========================== 在这里配置和写配置文件一样================
// .withClient("imooc") //第三方应用用户名
// .secret("imoocsecret") //密码
// .accessTokenValiditySeconds(7200)//token有效期
// .authorizedGrantTypes("password","refresh_token") //支持的授权模式
// .scopes("all","read","write") //相当于oauth的权限,这里配置了,请求里的必须和这里匹配
// //~=======如果有多个client,这里继续配置
// .and()
// .withClient("xxxxx"); //2,读取配置文件
InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
//判断是否配置了客户端
if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())){
for (OAuth2ClientProperties config : securityProperties.getOauth2().getClients()) {
builder.withClient(config.getClientId())
.secret(config.getClientSecret())
.accessTokenValiditySeconds(config.getAccessTokenValiditySeconds())
.authorizedGrantTypes("password","refresh_token") //这些也可以配置也可以写死,看心情
.scopes("all","read","write");
}
} }
}

重新获取token:

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

此时redis中就没有token信息了

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

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

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

<!-- 解析jwt  -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>

在controller里:

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

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

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. gj6 深入python的set和dict

    6.1 collections中的abc from collections.abc import Mapping, MutableMapping #dict属于mapping类型 a = {} pri ...

  2. redis之单机和主从环境搭建

    单机环境搭建 官网http://redis.io/download下载xxx.tar.gz二进制压缩包,注意下载2.8+版本,2.8之前的版本之前从服务器不支持部分重复制,2.6之前的版本不支持set ...

  3. Python 正斜杠/与反斜杠\

    首先,"/"左倾斜是正斜杠,"\"右倾斜是反斜杠,可以记为:除号是正斜杠一般来说对于目录分隔符,Unix和Web用正斜杠/,Windows用反斜杠,但是现在Wi ...

  4. 用Node完成AWS S3的Upload流程之全世界最简版

    开场: 查了两天文档,Error了38次,最后索性去掉所有附加条件, 连界面也不要了,在命令行里跑通了一坨最干瘪的Upload流程! 还冒着热气…… 在此先做记录,明天可以搭配美美的界面继续调试了. ...

  5. hdu 5685 Problem A (逆元)

    题目 题意:H(s)=∏i≤len(s)i=1(Si−28) (mod 9973),求一个字符串 子串(a 位到 b 位的)的哈希值.这个公式便是求字符串哈希值的公式,(字符的哈希值 = 字符的ASC ...

  6. node csv

    想实现下载csv文件的功能,内容放在mysql的blob中,在网上找的都是关于csv模块的. 由于csv的更新,网上的很多方法都用不了,比如csv(),现在已经变了:http://csv.adalta ...

  7. js 面向对象 定义对象

    js面向对象看了很多,却没有完全真正的理解,总是停留在一定的阶段,这次再认真看一下. 面向对象包含两种:定义类或对象:继承机制:都是通过工厂模式,构造函数,原型链,混合方法这四个阶段,原理也一样,只是 ...

  8. spring boot搭建Hello Word

    一.安装与配置jdk 二.安装与配置maven 安装好maven,必须配置环境变量 通过cmd命令查询maven是否安装成功,以下是安装成功的界面 修改setting.xml的配置,制定本地仓库的路径 ...

  9. eclipse中java build path下 allow output folders for source folders 无法勾选,该如何解决 eclipse中java build path下 allow output folders for source folders 无法勾选,

    在创建maven工程时,在设置output folders时,总是勾选以后,老是自动恢复到原来的状态,对比其他的maven的工程发现是在创建maven时候选择的项目为pom,而不是war或者jar,将 ...

  10. ios开发 ad hoc怎么用

    简单的说就是这样 ad hoc 方式是苹果用来给未上线的app做测试用的,首先你要在苹果开发平台上申请一个ad hoc的证书,再在profile中生成一个ad hoc 的profile文件(只需要在生 ...