SpringSecurityOAuth2 如何自定义token信息

 更新时间:2021年06月23日 10:18:04   作者:无小农  
这篇文章主要介绍了SpringSecurityOAuth2 自定义token信息的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

GitHub地址

码云地址

OAuth2默认的token返回最多只携带了5个参数(client_credentials模式只有4个 没有refresh_token)

下面是一个返回示例:

{
    "access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
    "token_type": "bearer",
    "refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
    "expires_in": 3599,
    "scope": "auth api"
}

然后我们需要的token可能需要增加username等自定义参数:

{
    "access_token": "1e93bc23-32c8-428f-a126-8206265e17b2",
    "token_type": "bearer",
    "refresh_token": "0f083e06-be1b-411f-98b0-72be8f1da8af",
    "expires_in": 3599,
    "scope": "auth api",
    "username":"username"
}

具体实现自定义token步骤如下: 新建一个自定义token信息的新建自定义token返回MyTokenEnhancer实现TokenEnhancer接口重写enhance方法:

/**
 * @Description 自定义token返回值
 * @Author wwz
 * @Date 2019/07/31
 * @Param
 * @Return
 */
public class MyTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();
        additionalInfo.put("username", user.getUsername());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        return accessToken;
    }
}

然后在认证服务配置AuthorizationServerEndpointsConfigurer中加上 MyTokenEnhancer。

这里划重点 因为我这里指定了了defaultTokenServices()所以得在这个方法里加上配置

还有,如果已经生成了一次没有自定义的token信息,需要去redis里删除掉该token才能再次测试结果,不然你的结果一直是错误的,因为token还没过期的话,是不会重新生成的。

Spring Security 使用JWT自定义Token

叙述

默认的token生成规则其实就是一个UUID,就是一个随机的字符串,然后存到redis中去,使用JWT的话,token中可以存放一些信息,我们服务端也不需要保存这个token, 服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证

使用JWT,在分布式系统中,很好地解决了单点登录问题,很容易解决了session共享的问题.但是是无法作废已颁布的令牌/不易应对数据过期,因为 token 并没有保存到服务端, 下面来看一下如何去配置JWT

配置JWT

TokenStoreConfig 这个类中要做一些修改,之前我们只在这个类里面配置了 redis 的存储,现在把 JWT 的配置也加上,如下:

@Configuration
public class TokenStoreConfig { 
    @Autowired
    private RedisConnectionFactory connectionFactory;
 
    @Bean
    @ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="redis")
    public TokenStore redisTokenStore(){
        return new RedisTokenStore(connectionFactory);
    } 
    @Configuration
    @ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="jwt", matchIfMissing = true)
    public static class JwtTokenConfig{
 
        @Autowired
        private SecurityProperties securityProperties;
 
        @Bean
        public TokenStore jwtTokenStore(){
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
 
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter(){
            // token生成中的一些处理
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey(securityProperties.getOAuth2().getJwtTokenSignKey());
            return converter;
        } 
    }
}

这里首先是一个内部的静态类 JwtTokenConfig 用来配置 JWT 的一些配置,第一个方法 jwtTokenStore() 就是配置token的存储,然后这里需要一个 JwtAccessTokenConverter 因为 TokenStore 只管 Token 的存储,生成规则还需要配置,所以 jwtAccessTokenConverter() 就是用来做一些 Token 的处理

这个类上有一个注解

@ConditionalOnProperty(prefix = "core.security.oAuth2", name="tokenStore", havingValue="jwt", matchIfMissing = true)

意思就是,在配置中有 core.security.oAuth2.tokenStore 这个配置,而且值是 jwt 的话,就生效,最后有一个 matchIfMissing = true ,这个表示, 如果配置中没有这个配置的话,也生效

上面的 redisTokenStore 也加了这个注解,但是没有 matchIfMissing 默认是 false, 总的配置就是如果在配置中没有指定哪种 tokenStore 的话,就默认的用 jwt ,如果想要使用 redis 存储的话,必须明确的指定 core.security.oAuth2.tokenStore: redis

最后认证服务的配置中还需要做一些修改

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 
    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter; 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        
        // ... 省略其他配置
 
        // 只有配置使用JWT的时候才会生效
        if (jwtAccessTokenConverter != null) {
            endpoints.accessTokenConverter(jwtAccessTokenConverter);
        }
     }
 
    // ... 省略其他代码
}

这里,就是给 endpoints 指定一下 JwtAccessTokenConverter 就可以了

测试

请求 Token 还是跟之前的方式一样的, 如下:

可以看到这里的token就是使用jwt了

在 jwt.io 中可以把刚刚生成的token解析一下,内容如下:

这个就是我们生成的 JWT 中包含的信息

TokenEnhancer 的使用

TokenEnhancer 是一个增强器,JWT 中是可以放一些我们自定义的信息的,如果要加入一些我们自己的信息的话,就得使用 TokenEnhancer

还是修改 TokenStoreConfig ,在 JwtTokenConfig 这个内部类中,加一个配置

@Bean
@ConditionalOnBean(TokenEnhancer.class)
public TokenEnhancer jwtTokenEnhancer(){
    return new MyJwtTokenEnhancer();
}

然后 MyJwtTokenEnhancer 代码如下:

public class MyJwtTokenEnhancer implements TokenEnhancer { 
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> info = new HashMap<>(1);
        info.put("custom", "test");
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        return accessToken;
    } 
}

这里加了 @ConditionalOnBean,是必须存在一个TokenEnhancer 的时候,才被创建, 之前的 JwtAccessTokenConverter 也是一个 TokenEnhancer

最后,认证服务器配置类 MyAuthorizationServerConfig 中,需要修改一下

@Autowired(required = false)
private TokenEnhancer jwtTokenEnhancer; 
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
 
    endpoints.authenticationManager(authenticationManager);
    endpoints.userDetailsService(userDetailsService);
    endpoints.tokenStore(tokenStore);
 
    // 只有配置使用JWT的时候才会生效
    if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>();
        enhancers.add(jwtTokenEnhancer);
        enhancers.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(enhancers);
        endpoints.tokenEnhancer(enhancerChain)
                .accessTokenConverter(jwtAccessTokenConverter);
    }
}

测试

获取token,然后解析结果如下:

自定义数据解析

这里 Spring 在解析JWT的时候会解析成一个 Authentication 对象,并不会解析我们上面设置的自定义字段,这个还需要我们自己去解析

demo 项目中增加一个 jwt 解析的依赖

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

然后再 /user/me 这个接口中做解析,代码如下:

@GetMapping("/me")
public Object me(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
    // 从请求头中获取到token
    String jwtToken = StringUtils.substringAfter(request.getHeader("Authorization"), AUTHORIZATION_PREFIX);
    log.info("请求头中的token:{}", jwtToken);
 
    // 获取配置中的 jwtTokenSignKey
    String jwtTokenSignKey = securityProperties.getOAuth2().getJwtTokenSignKey();
    Claims claims = Jwts.parser().setSigningKey(jwtTokenSignKey.getBytes("UTF-8")).parseClaimsJws(jwtToken).getBody();
    return claims;
}

效果如下:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java版学生管理系统

    Java版学生管理系统

    这篇文章主要为大家详细介绍了Java版学生管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • java读取wav文件(波形文件)并绘制波形图的方法

    java读取wav文件(波形文件)并绘制波形图的方法

    这篇文章主要介绍了java读取wav文件(波形文件)并绘制波形图的方法,涉及java操作多媒体音频文件转换的相关技巧,需要的朋友可以参考下
    2015-06-06
  • Java代码生成器的制作流程详解

    Java代码生成器的制作流程详解

    这篇文章主要介绍了Java代码生成器的制作流程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • RestTemplate报错400 Bad Request的解决方案

    RestTemplate报错400 Bad Request的解决方案

    在使用Spring Boot时,若直接通过@Autowired注入RestTemplate可能会遇到400BadRequest错误,原因在于Spring Boot官方文档指出,由于RestTemplate实例通常需要在使用前进行定制,因此Spring Boot不会自动配置单个RestTemplate Bean
    2024-11-11
  • Spring中@Autowired自动注入map详解

    Spring中@Autowired自动注入map详解

    这篇文章主要介绍了Spring中@Autowired自动注入map详解,  spring是支持基于接口实现类的直接注入的,支持注入map,list等集合中,不用做其他的配置,直接注入,需要的朋友可以参考下
    2023-10-10
  • IntelliJ IDEA 2018 最新激活码(截止到2018年1月30日)

    IntelliJ IDEA 2018 最新激活码(截止到2018年1月30日)

    这篇文章主要介绍了IntelliJ IDEA 2018 最新激活码(截止到2018年1月30日)的相关资料,需要的朋友可以参考下
    2018-01-01
  • 从千千静听歌词服务器获取lrc歌词示例分享

    从千千静听歌词服务器获取lrc歌词示例分享

    这篇文章主要介绍了使用PHP从千千静听歌词服务器获取lrc歌词的方法,大家参考使用吧
    2014-01-01
  • SparkSQL中的JSON内置函数全解析

    SparkSQL中的JSON内置函数全解析

    你是否曾经为处理JSON数据而头疼?SparkSQL为我们提供了强大的内置JSON函数,让JSON处理变得轻而易举,本文将带你深入了解这些函数,感兴趣的朋友一起看看吧
    2024-08-08
  • 关于JWT之token令牌认证登录

    关于JWT之token令牌认证登录

    这篇文章主要介绍了关于JWT之token令牌认证登录,使用JWT能够保证Token的安全性,且能够进行Token时效性的检验,使用JWT时,登录成功后将用户信息生成一串令牌字符串,需要的朋友可以参考下
    2023-05-05
  • SpringBoot整合Hmily实现TCC分布式事务

    SpringBoot整合Hmily实现TCC分布式事务

    这篇文章主要为大家详细介绍了SpringBoot如何整合Hmily实现TCC分布式事务,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-11-11

最新评论