使用JWT替换默认令牌token

    xiaoxiao2022-07-13  152

    文章目录

    JSON Web Token (JWT)自包含token的创建总结 密签可扩展替换默认tokenTokenStoreConfigWhaleAuthenticationServiceConfigOAuth2Properties测试 jwt jwt token加额外信息实现TokenEnhancerTokenStoreConfig 配置 TokenEnhancer beanWhaleAuthenticationServiceConfig 中配置tokenEnhancerChain测试Authentication解析额外信息?demo pom 文件中加依赖解析额外信息 UserController测试 token令牌的刷新refresh_token测试

    JSON Web Token (JWT)

    自包含

    token的创建

    org.springframework.security.oauth2.provider.token.DefaultTokenServices#createAccessToken(org.springframework.security.oauth2.provider.OAuth2Authentication, org.springframework.security.oauth2.common.OAuth2RefreshToken)

    总结

    可见token根据UUID生成的 所以token里面的信息是无意义的 看上节redis里面token的存储,是把相关信息与token做了关联 如过redis服务器挂掉了,那么这个token就是毫无意义的,以为他本身不包含任何信息

    jwt 恰恰相反,它是自包含了相关的信息

    密签

    密签的意思 不是 jwt 信息的加密或解密

    他是指如果 jwt 里面的信息被修改以后 我们能够通过密签发现

    jwt不能存储敏感信息

    可扩展

    可以在里面放任何数据

    替换默认token

    TokenStoreConfig

    这个配置里面配置了两个TokenStore 根据不同的配置确认token的类型 默认是JwtTokenStore

    @Configuration public class TokenStoreConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "redis",matchIfMissing = false) public TokenStore tokenStore(){ return new RedisTokenStore(redisConnectionFactory); } @Configuration @ConditionalOnProperty(prefix = "whale.security.oauth2",name = "storeType",havingValue = "jwt",matchIfMissing = true) //prefix 是只application配置文件中 配置的最后一个点分隔后前面的一部分 这叫前缀 //name 对应的是后缀 最后一个点分隔后的后面的那部分 //即检查的配置项为 whale.security.oauth2.storeType //havingValue = "jwt" 当配置项的值为jwt的时候 这个类里面的所有配置生效 //matchIfMissing 如果配置文件里没有找到这个属性,即默认生效 public static class JwtTokenConfig{ @Autowired private SecurityProperties securityProperties; //token 的存储 @Bean public TokenStore jwtTokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter()); } //token生成处理 @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setSigningKey(securityProperties.getOauth2().getJwtSigningKey()); return jwtAccessTokenConverter; } } }

    WhaleAuthenticationServiceConfig

    OAuth2Properties

    /** * 这个是秘钥 一定要保存好 * * 发出去的令牌要用它签名 * 收到的令牌也要用它来验签 * * jwt 是token的标准协议,唯一的安全性就是这个秘钥 * */ private String jwtSigningKey = "whale";

    测试 jwt

    用表单登录获取token的方式 发现 这次的 access_token 很长

    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTg2MDg5NjQsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iLCJST0xFX1VTRVIiXSwianRpIjoiMzYzNjJiZWUtMjFjOS00ZWM3LWIxMmUtYjQyYzYyYzViZjI5IiwiY2xpZW50X2lkIjoid2hhbGUiLCJzY29wZSI6WyJhbGwiLCJyZWFkIiwid3JpdGUiXX0.PJ1vcPCccfVfrHxCJzRs19xoFV6hnu5lMetJuYARylw",

    https://jwt.io/ jwt解析 访问资源成功

    jwt token加额外信息

    实现TokenEnhancer

    WhaleTokenEnhancer

    public class WhaleTokenEnhancer implements TokenEnhancer { /** * * @param oAuth2AccessToken * @param oAuth2Authentication * @return */ @Override public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) { Map<String,Object> info = new HashMap<>(); info.put("company","埃森哲"); ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info); return oAuth2AccessToken; } }

    TokenStoreConfig 配置 TokenEnhancer bean

    ······························· public static class JwtTokenConfig{ ····················· @Bean @ConditionalOnMissingBean(name = "jwtTokenEnhancer") public TokenEnhancer jwtTokenEnhancer(){ return new WhaleTokenEnhancer(); } }

    WhaleAuthenticationServiceConfig 中配置tokenEnhancerChain

    部分代码如下

    @Autowired(required = false) private TokenEnhancer jwtTokenEnhancer; /** * * @param endpoints oauth/token 的入口点 * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { // super.configure(endpoints); // endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService); endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); if(jwtAccessTokenConverter!=null && jwtTokenEnhancer!=null){ TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancers = new ArrayList<>(); enhancers.add(jwtTokenEnhancer); enhancers.add(jwtAccessTokenConverter); tokenEnhancerChain.setTokenEnhancers(enhancers); endpoints.tokenEnhancer(tokenEnhancerChain) .accessTokenConverter(jwtAccessTokenConverter); } }

    测试

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCIsInJlYWQiLCJ3cml0ZSJdLCJjb21wYW55Ijoi5ZD5qOu5ZOyIiwiZXhwIjoxNTU4NjEzNjQzLCJhdXRob3JpdGllcyI6WyJhZG1pbiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI5OTA3NWZjOC04MTI3LTQ2YWYtYTM2OS0wNTlkODlkNTIwMTEiLCJjbGllbnRfaWQiOiJ3aGFsZSJ9.1Ild0mbgxQn1No0NQIQI2gWrTDFDcgdHYHdC_kdGq_w

    解析如下

    Authentication解析额外信息?

    注意 虽然我们的jwt可以携带额外信息 但是 我们的spring 只是 将 jwt规范里面的信息解析成 Authentication 如果我们需要jwt token 里的额外信息呢?

    为此我们需要加入新的依赖

    https://jwt.io/ https://www.jsonwebtoken.io/

    demo pom 文件中加依赖

    试了几个 0.9.0 版本可以

    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>

    解析额外信息 UserController

    @GetMapping("/me11") public Object getCurrentUser11(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException { //spring 会自动找到Authentication类型的数据注入 String header = request.getHeader("Authorization"); String token = StringUtils.substringAfter(header, "Bearer "); //需要用秘钥来验证签名 //注意 签名的时候 默认用的是秘钥的utf-8编码 ,同时验签的时候也要知道 编码 utf-8 Claims claims = Jwts.parser().setSigningKey(securityProperties.getOauth2().getJwtSigningKey().getBytes("utf-8")) .parseClaimsJws(token).getBody(); String company = (String) claims.get("company"); System.out.println(company); return authentication; }

    测试

    token令牌的刷新

    refresh_token

    在用户无感的情况下用refresh_token得到一个新的token

    测试

    但我的refresh_token没有测试成功 也不知道什么原因

    按道理是完全可以的,也不知道问题出在哪

    https://auth0.com/docs/tokens/refresh-token/current#9e51c5c22d4b450cab209018dc3be588_java

    Use a Refresh Token To exchange the Refresh Token you received during authorization for a new Access Token, make a POST request to the /oauth/token endpoint in the Authentication API, using grant_type=refresh_token.

    curl --request POST \ --url 'https://YOUR_DOMAIN/oauth/token' \ --header 'content-type: application/x-www-form-urlencoded' \ --data 'grant_type=refresh_token&client_id=${account.clientId}&client_secret=YOUR_CLIENT_SECRET&refresh_token=YOUR_REFRESH_TOKEN'

    https://openapi.baidu.com/wiki/index.php?title=使用Refresh_Token获取Access_Token

    https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

    https://auth0.com/docs/tokens/refresh-token/current

    https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

    https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#acquiring--client-ids-and-secrets

    最新回复(0)