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;
}

效果如下:

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

(0)

相关推荐

  • Springboot+SpringSecurity+JWT实现用户登录和权限认证示例

    如今,互联网项目对于安全的要求越来越严格,这就是对后端开发提出了更多的要求,目前比较成熟的几种大家比较熟悉的模式,像RBAC 基于角色权限的验证,shiro框架专门用于处理权限方面的,另一个比较流行的后端框架是Spring-Security,该框架提供了一整套比较成熟,也很完整的机制用于处理各类场景下的可以基于权限,资源路径,以及授权方面的解决方案,部分模块支持定制化,而且在和oauth2.0进行了很好的无缝连接,在移动互联网的授权认证方面有很强的优势,具体的使用大家可以结合自己的业务场景进行选

  • SpringSecurity Jwt Token 自动刷新的实现

    功能需求 最近项目中有这么一个功能,用户登录系统后,需要给 用户 颁发一个 token ,后续访问系统的请求都需要带上这个 token ,如果请求没有带上这个 token 或者 token 过期了,那么禁止访问系统.如果用户一直访问系统,那么还需要自动延长 token 的过期时间. 功能分析 1.token 的生成 使用现在比较流行的 jwt 来生成. 2.token 的自动延长 要实现 token 的自动延长,系统给用户 颁发 一个 token 无法实现,那么通过变通一个,给用户生成 2个 t

  • SpringBoot集成Spring Security用JWT令牌实现登录和鉴权的方法

    最近在做项目的过程中 需要用JWT做登录和鉴权 查了很多资料 都不甚详细 有的是需要在application.yml里进行jwt的配置 但我在导包后并没有相应的配置项 因而并不适用 在踩过很多坑之后 稍微整理了一下 做个笔记 一.概念 1.什么是JWT Json Web Token (JWT)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519) 该token被设计为紧凑且安全的 特别适用于分布式站点的单点登录(SSO)场景 随着JWT的出现 使得校验方式更加简单便

  • SpringSecurityOAuth2 如何自定义token信息

    GitHub地址 码云地址 OAuth2默认的token返回最多只携带了5个参数(client_credentials模式只有4个 没有refresh_token) 下面是一个返回示例: { "access_token": "1e93bc23-32c8-428f-a126-8206265e17b2", "token_type": "bearer", "refresh_token": "0f083e

  • 使用SpringSecurity 进行自定义Token校验

    背景 Spring Security默认使用「用户名/密码」的方式进行登陆校验,并通过cookie的方式存留登陆信息.在一些定制化场景,比如希望单独使用token串进行部分页面的访问权限控制时,默认方案无法支持. 在未能在网上搜索出相关实践的情况下,通过官方文档及个别Stack Overflow的零散案例,形成整体思路并实践测试通过,本文即关于该方案的一个分享. 参考官方文档 SpringSecurity校验流程 基本的SpringSecurity使用方式网上很多,不是本文关注的重点. 关于校验

  • vue自定义键盘信息、监听数据变化的方法示例【基于vm.$watch】

    本文实例讲述了vue自定义键盘信息.监听数据变化的方法.分享给大家供大家参考,具体如下: @keydown.up @keydown.enter @keydown.a/b/c.... 自定义键盘信息: Vue.directive('on').keyCodes.ctrl=17; Vue.directive('on').keyCodes.myenter=13; @keydown.a/b/c.... <input type="text" @keydown.c="show&quo

  • laravel dingo API返回自定义错误信息的实例

    laravel 在使用了 dingo API 后,错误信息被dingo异常类接管了,返回信息变成了 : 要返回自定义的错误信息,就需要再把错误异常类接管回来(大概这个意思...) 方法: 在 app\Providers\AppServiceProvider.php 中的 boot() 方法 添加如下代码: app('api.exception')->register(function (\Exception $exception) { $request = Request::capture();

  • js 使用ajax设置和获取自定义header信息的方法小结

    本文实例讲述了js 使用ajax设置和获取自定义header信息的方法.分享给大家供大家参考,具体如下: 1.js ajax 设置自定义header 1.1 方法一: $.ajax({ type: "POST", url: "Handler1.ashx", contentType: "application/x-www-form-urlencoded", beforeSend: function (request) { request.setRe

  • 使用BindingResult 自定义错误信息

    目录 BindingResult 自定义错误信息 前提概要 基础框架 配置文件和Java代码的修改 在Controller方法中指定需要进行校验 进行自定义校验 在JSP页面上显示校验错误信息 BindingResult错误返回显示失败 BindingResult 自定义错误信息 前提概要 在Spring MVC和FreeMarker整合的项目中,采用JSR-303验证框架,通过注解的方式进行数据验证 基础框架 MVC:Spring MVC 3 视图:FreeMarker 验证:Hibernat

  • log4j日志格式加入自定义字段信息方式

    目录 log4j日志格式加入自定义字段信息 log4j2入库自定义字段类型 官方API文档中只提供了几个属性 设置isNumber="true" log4j日志格式加入自定义字段信息 在使用log4j日志的时候,有时需要在日志中加入自定义字段信息,例如在日志中加入登录用户的信息等,这时就可以使用org.apache.log4j.MDC来实现该功能: 1.在Action的方法中,直接使用 MDC.put("username",getUserInfo().getName

  • .NET Core授权失败自定义响应信息的操作方法

    前言 在.NET 5之前,当授权失败即403时无法很友好的自定义错误信息,以致于比如利用Vue获取到的是空响应,不能很好的处理实际业务,同时涉及到权限粒度控制到控制器.Action,也不能很好的获取对应路由信息.本文我们来看看在.NET 5中为何要出现针对授权失败的中间件接口?它是如何一步步衍生出来的呢?以及对于授权失败根据实际需要如何自定义响应错误,以及如何获取对应路由信息等等 授权失败自定义响应信息 如下是在.NET 5之前,对于授权处理,我们大多实现自定义的AuthorizationHan

  • Spring Cloud OAuth2实现自定义token返回格式

    目录 问题描述 解决方案 总结 最近读者朋友针对Spring Security OAuth2.0 想要陈某补充一些知识,如下: 今天这篇文章就来回答其中一个问题:如何自定义token的返回格式? 问题描述 Spring Security OAuth的token返回格式都是默认的,但是往往这个格式是不适配系统,/oauth/token返回的格式如下: { "access_token": token "token_type": "bearer", &

  • Vue前端登录token信息验证功能实现

    用户在首次访问网站时,应在登录页面填写账号密码,前端携带用户信息向服务器请求. 1.服务器验证用户信息 验证失败:给前端响应数据 验证通过:对该用户创建token,并以响应数据返回给前端 2.前端接受后端响应的数据 错误信息:提示错误消息 正确信息:页面进行跳转至首页,同时保存token(可以保存在cookie或localstorage) 3.用户点击某功能模块的触发请求(比如某功能有权限设置或是是否需要token) 利用路由守卫beforeEach() 将保存的token添加至请求拦截器的请求

随机推荐