首页 > 其他分享 >SpringSecurity OAuth2 关于 UserAuthenticationConverter

SpringSecurity OAuth2 关于 UserAuthenticationConverter

时间:2022-12-01 17:35:47浏览次数:49  
标签:map OAuth2 return JWT private SpringSecurity Token UserAuthenticationConverter p

前言

之前写了一大堆关于SpringSecurity OAuth2的相关文章,本以为可以告一段落了,但是有了解到一个新东西,之前没注意到的,就是UserAuthenticationConverter,本章就来看看这是个啥玩意!我们知道授权服可以提供两种格式的Token,一种是默认的字符串,另一种是JWT格式的字符串,关于JWT格式Token我这里就不多逼逼是什么意思勒,我们认证服采用JWT格式主要就是为了提高Token校验的性能,如果我们采用默认的字符串的话默认需要调用认证服进行token检查,就是通过Http请求的方式调用认证服中的接口,那么这样大大限制了我们鉴权,我们采用JWT的话那么就可以让资源服进行本地鉴权,不用通过Http方式调用授权服进行Token的认证,那么这些都是没问题的,这也是本文的铺垫,我们知道JWT可以自包含一些数据的,那么我们在生成Token的时候就可以自定义一些数据进去,那么这部分之前文章中也有写过SpringSecurityOAuth2采用JWT生成Token的模式自定义JWT数据,那么我们将数据是放入到Token中了,也能将其获取到,SpringSecurity OAuth使用JWT替换默认Token这篇文章中有怎么获取,但是回过头看过去的文章感觉有点lol,那么本文我们就通过UserAuthenticationConverter玩意让我们更高级的获取JWT中的自定义数据!之前那种方式是通过解析Token中的字符串来实现的,那么我们高级点的办法突破口是在Principal,这里先提前透露一下

默认情况

默认获取Principal

	@GetMapping(value = "/getPrincipal")
    public R getPrincipal(Authentication authentication) {
        return R.ok(authentication.getPrincipal());
    }

这里得到的是用户名,对默认就是只包含用户名,我们看看解析流程

解析流程

OAuth2AuthenticationProcessingFilter
请求先是来到我们的老熟人OAuth2AuthenticationProcessingFilter
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java
OAuth2AuthenticationManager
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java_02
DefaultTokenServices
SpringSecurity OAuth2 关于 UserAuthenticationConverter_数据_03
DefaultAccessTokenConverter
SpringSecurity OAuth2 关于 UserAuthenticationConverter_http_04
注意这里就已经得到了我们的自定义数据了!
回到主流程
SpringSecurity OAuth2 关于 UserAuthenticationConverter_spring_05
DefaultAccessTokenConverter
SpringSecurity OAuth2 关于 UserAuthenticationConverter_数据_06
DefaultUserAuthenticationConverter
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java_07
这里也就是核心了,至于我们的principal中为什么只有用户名,核心点就在这里!理论上我们只需要将解析是的principal更换成我们自定义的对象,那么我们获取principal的时候就可以同理得到!

改造

创建CustomUserAuthenticationTokenConverter

public class CustomUserAuthenticationTokenConverter implements UserAuthenticationConverter {

    private static final String N_A = "N/A";

    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        Map<String, Object> response = new LinkedHashMap<>();
        response.put(USERNAME, authentication.getName());
        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
            response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
        }
        return response;
    }


    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        if (map.containsKey(USERNAME)) {
            Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
            JSONObject jsonObject = new JSONObject();
            //自定义对象===>实际上这个对象是从map中获取对应的值,然后组装成自己需要的用户对象即可
            jsonObject.put("aaa", "aaa");
            jsonObject.put("bbb", "bbb");
            return new UsernamePasswordAuthenticationToken(jsonObject, N_A, authorities);
        }
        return null;
    }


    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        Object authorities = map.get(AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(
                    StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        return AuthorityUtils.NO_AUTHORITIES;
    }


}

创建CustomResourceServiceTokenServices

public class CustomResourceServiceTokenServices implements ResourceServerTokenServices {


    @Setter
    private TokenStore tokenStore;

    @Setter
    private DefaultAccessTokenConverter defaultAccessTokenConverter;

    @Setter
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Override
    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
        UserAuthenticationConverter userTokenConverter = new CustomUserAuthenticationTokenConverter();
        defaultAccessTokenConverter.setUserTokenConverter(userTokenConverter);
        Map<String, ?> map = jwtAccessTokenConverter.convertAccessToken(readAccessToken(accessToken), oAuth2Authentication);
        return defaultAccessTokenConverter.extractAuthentication(map);
    }

    @Override
    public OAuth2AccessToken readAccessToken(String accessToken) {
        return tokenStore.readAccessToken(accessToken);
    }
}

配置ResourceServerTokenServices

@Configuration
public class ResourceServerTokenServicesConfig {

    @Autowired
    private TokenStore tokenStore;

    @Autowired(required = false)//required,只有在Jwt模式下才生效,正常情况下是没有这个类的
    private JwtAccessTokenConverter jwtAccessTokenConverter;


    //令牌管理服务
    @Bean
    public ResourceServerTokenServices tokenService() {
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        UserAuthenticationConverter userTokenConverter =new CustomUserAuthenticationTokenConverter();
        accessTokenConverter.setUserTokenConverter(userTokenConverter);

        CustomResourceServiceTokenServices tokenServices = new CustomResourceServiceTokenServices();
        tokenServices.setDefaultAccessTokenConverter(accessTokenConverter);

        tokenServices.setTokenStore(tokenStore);
        tokenServices.setJwtAccessTokenConverter(jwtAccessTokenConverter);
        return tokenServices;

    }
}

注入ResourceServerTokenServices

	@Autowired
    private ResourceServerTokenServices tokenService;
    
     @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        log.info("RESOURCE_ID===>" + RESOURCE_ID);

        resources.resourceId(RESOURCE_ID)//资源 id
                .authenticationEntryPoint(customAuthenticationEntryPointHandler)//匿名用户访问无权限资源时的异常处理器
                .accessDeniedHandler(customAccessDeniedHandler)//认证过的用户访问无权限资源时的异常处理器
                .tokenServices(tokenService)
                .stateless(true);
    }

重启测试!
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java_08
搞定了!其实这里我觉得是有点徒劳,真的,因为在我写这篇文章的时候注意到之前代码中有如下这些代码!
SpringSecurity OAuth2 关于 UserAuthenticationConverter_spring_09
文章链接SpringSecurityOAuth2获取JWT中的数据

注意

在编写CustomResourceServiceTokenServices的时候,上面代码可以更加严谨点,我们看看默认实现的
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java_10
建议也加上这些判断!
SpringSecurity OAuth2 关于 UserAuthenticationConverter_spring_11
否则即使accessToken、result为空,或者解析失败那么也不会被我们的异常处理器捕获,这里指的异常处理器是
SpringSecurity OAuth2 关于 UserAuthenticationConverter_spring_12
SpringSecurity OAuth2 关于 UserAuthenticationConverter_spring_13
注意注意!!!

写在最后

这种写法后面经过本人实际业务开发调试,其实觉得有点鸡肋,而且带来很多开发上的问题,包括性能的损失!
1.丢失details
SpringSecurity OAuth2 关于 UserAuthenticationConverter_json_14
也就是defaultAccessTokenConverter.extractAuthentication(map);行代码执行完和SpringSecurity OAuth2默认执行完获取到的OAuth2Authentication中的数据是不一样的,会丢失details!

2.性能损耗
SpringSecurity OAuth2 关于 UserAuthenticationConverter_数据_15
通过追溯上面丢失details的问题,源码级别的调试发现框起来的两段代码出现重复执行的问题,我们做这个UserAuthenticationConverter的操作,无非就是扩展principal对象的,实际也就是这部分代码调用开始扩展的!
SpringSecurity OAuth2 关于 UserAuthenticationConverter_http_16
通过这个方法,就可以执行到扩展代码
SpringSecurity OAuth2 关于 UserAuthenticationConverter_json_17
然而就是这里的extractAuthentication方法在OAuth2Authentication result = tokenStore.readAuthentication(accessToken);处就已经执行过了
SpringSecurity OAuth2 关于 UserAuthenticationConverter_java_18
只不过userTokenConverter.extractAuthentication(map);
SpringSecurity OAuth2 关于 UserAuthenticationConverter_http_19
调用的是SpringSecurity OAuth2默认的实现罢了!

标签:map,OAuth2,return,JWT,private,SpringSecurity,Token,UserAuthenticationConverter,p
From: https://blog.51cto.com/u_15899048/5903403

相关文章

  • SpringSecurityOAuth2授权流程源码分析(自定义验证码模式)
    前言周末闲来无事,谢谢自己的项目,然后想把老的授权模式改造一下,老的是基于SpringSecurity的实现,想升级为SpringSecurityOAuth2模式,于是看了下之前搭建的SpringSecurityO......
  • SpringSecurity之授权
    回顾之前:Jwt解决的是认证的问题(我是谁),但是在SpringSecurity中最受欢迎的是授权(我能做哪些事情?)1.AccessDecisionManager2.安全表达式,越广泛适用的规则需......
  • SpringSecurity多表验证
    在开始之前我想感叹一句,技术久了不回头看看真的会忘记的,这次公司让我重新开发一个程序,项目架构为单体多模块开发,其中有个需求就是需要不同用户表进行登录,且不同表的用户名......
  • SpringSecurity从入门到精通
    0.简介SpringSecurity和Shiro比较。中大型的项目都是使用SpringSecurity做安全框架,小项目使用Shiro比较多,因为它比SpringSecurity上手更加简单认证与授权:认证:验证......
  • Spring Cloud 最新版发布,Spring Security + OAuth2 终于安排上了!
    大家好,我是栈长。今天给大家通报一则框架更新消息,时隔两个月,SpringCloud2021.0.5最新版发布了,来看下最新的SpringCloud版本情况:SpringCloud无疑是现在Java微服务......
  • 5.OAuth2详解
     理论OAuth是一个关于授权(authorization)的开放网络标准,用来授权第三方应用获取用户数据,是目前最流行的授权机制,它当前的版本是2.0。应用场景假如你正在“网站A”上冲......
  • SpringSecurity之JwtFilter-实现独立认证过滤器
    前言:之前做的项目都是框架封装好的认证过程,空余时间复习一下。1.访问令牌2.刷新令牌3.令牌如何存放?  3.1:store(放内存中),刷新浏览器会有重新登录的问题。  3.2:......
  • SpringSecurity-从入门到精通-三更草堂
    SpringSecurity从入门到精通课程介绍0.简介​ SpringSecurity是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比......
  • SpringSecurity登录时报错栈溢出,StackOverflow,SpringSecurity多端登录实现方案
    最近在用springsecurity做一套医疗项目,要求一套后端对应两套前端界面,用户(患者)和医生。先写的用户登录界面,没有问题,再用同样方法写医生登录的时候报错栈溢出stackoverfl......
  • 微服务篇之SpringSecurity。
    1.SpringSecurity目前主流的认证授权的框架流行程度 TRANSLATEwithxEnglishArabicHebrewPolishBulgarianHindiPortugueseCatalanHmongDaw......