当前位置:   article > 正文

OAuth2资源服务器验证源码分析_authorities must be either a string or a collectio

authorities must be either a string or a collection

目录

资源服务器配置类

校验服务器字段配置

jwt校验路径追踪

OAuth2AuthenticationProcessingFilter

OAuth2AuthenticationManager

DefaultTokenServices

第一次JwtTokenStore

第一次JwtAccessTokenConverter

传回DefaultTokenServices

第二次JwtTokenStore

第二次JwtAccessTokenConverter

DefaultAccessTokenConverter

DefaultUserAuthenticationConverter

回到DefaultAccessTokenConverter

再次传回DefaultTokenServices

传回OAuth2AuthenticationManager

传回OAuth2AuthenticationProcessingFilter

jwt校验总结

@PreAuthorize中hasAuthority怎么获取权限的?

SecurityExpressionRoot怎么生成的?


资源服务器配置类

这里因为配置了TokenStore 和JwtAccessTokenConverter ,所以下面解析jwt使用的配置是这个

  1. @Configuration
  2. @EnableResourceServer
  3. @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的 PreAuthorize注解
  4. public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
  5. //公钥
  6. private static final String PUBLIC_KEY = "publickey.txt";
  7. //定义JwtTokenStore,使用jwt令牌
  8. @Bean
  9. public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
  10. return new JwtTokenStore(jwtAccessTokenConverter);
  11. }
  12. //定义JJwtAccessTokenConverter,使用jwt令牌
  13. @Bean
  14. public JwtAccessTokenConverter jwtAccessTokenConverter() {
  15. JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
  16. converter.setVerifierKey(getPubKey());
  17. return converter;
  18. }
  19. /**
  20. * 获取非对称加密公钥 Key
  21. * @return 公钥 Key
  22. * */
  23. private String getPubKey() {
  24. Resource resource = new ClassPathResource(PUBLIC_KEY);
  25. try {
  26. InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
  27. BufferedReader br = new BufferedReader(inputStreamReader);
  28. return br.lines().collect(Collectors.joining("\n"));
  29. } catch (IOException ioe) {
  30. return null;
  31. }
  32. }
  33. //Http安全配置,对每个到达系统的http请求链接进行校验
  34. @Override
  35. public void configure(HttpSecurity http) throws Exception {
  36. //所有请求必须认证通过
  37. http.authorizeRequests()
  38. .antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui",
  39. "/swagger-resources","/swagger-resources/configuration/security",
  40. "/swagger-ui.html","/webjars/**","/course/courseview/**").permitAll()//针对swagger-ui进行放行
  41. .anyRequest().authenticated();
  42. }
  43. }

校验服务器字段配置

  1. /**
  2. * 自定义token显示内容
  3. */
  4. @Component
  5. public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
  6. @Autowired
  7. UserDetailsService userDetailsService;
  8. @Override
  9. public Map<String, ?> convertUserAuthentication(Authentication authentication) {
  10. LinkedHashMap response = new LinkedHashMap();
  11. String name = authentication.getName();
  12. response.put("user_name", name);
  13. Object principal = authentication.getPrincipal();
  14. UserJwt userJwt = null;
  15. if(principal instanceof UserJwt){
  16. userJwt = (UserJwt) principal;
  17. }else{
  18. //refresh_token默认不去调用userdetailService获取用户信息,这里我们手动去调用,得到 UserJwt
  19. UserDetails userDetails = userDetailsService.loadUserByUsername(name);
  20. userJwt = (UserJwt) userDetails;
  21. }
  22. response.put("name", userJwt.getName());
  23. response.put("id", userJwt.getId());
  24. response.put("utype",userJwt.getUtype());
  25. response.put("userpic",userJwt.getUserpic());
  26. response.put("companyId",userJwt.getCompanyId());
  27. if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
  28. response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
  29. }
  30. return response;
  31. }
  32. }

jwt校验路径追踪

当已经登录成功,得到token进行请求资源服务器

OAuth2AuthenticationProcessingFilter

首先会被

过滤器进行拦截,通过请求header得到JWT token,将token封装到Authorication中

然后调用

的authenticate方法

进行验证解析jwt Token

OAuth2AuthenticationManager

authenticate会通过Authorization得到jwtToken,并将jwtToken作为参数调用

的loadAuthentication方法

DefaultTokenServices

loadAuthentication会调用

的readAccessToken方法,读取解析token中的内容

第一次JwtTokenStore

readAccessToken会调用convertAccessToken,方法体内会调用

经过配置文件中配置的公钥进行解析jwt Token中的内容得到 jwt中 claim的map集合(包含所有jwt的可可视信息,DefaultUserAuthenticationConverter中配置的属性项)

并且将 map集合和jwt Token 作为参数 继续调用JwtAccessTokenConverter的extractAccessToken

第一次JwtAccessTokenConverter

extractAccessToken会往下传递调用

的extractAccessToken方法。

进行map集合的数据格式整理,最后封装到

然后传回

传回DefaultTokenServices

接着会将上面传回的OAuth2AccessToken作为参数调用

的readAuthentication方法 进行权限读取。

第二次JwtTokenStore

readAuthentication会将OAuth2AccessToken中的jwt Token取出

进行解析信息后将map传入

extractAuthentication方法

第二次JwtAccessTokenConverter

紧接着会像上面过程一样。会将参数传递调用

的extractAuthentication方法

 

DefaultAccessTokenConverter

extractAuthentication方法中会将map作为参数传入

的extractAuthentication方法 进行权限封装

DefaultUserAuthenticationConverter

2个方法主要作用是判断map中是否有用户名(user_name)和权限(authorities)2个属性。如果没有user_name直接返回为null,反之返回由这2个属性封装好的UsernamePasswordAuthenticationToken。

(可以在配置类配置userDetailsService,配置后可以进行通过loadUserByUserName校验然后更改原有的权限和用户。)

这里需要注意 user_name和authorities是固定好的名字,所以在DefaultUserAuthenticationConverter中需要注意传入jwt claim的参数名。

  1. public Authentication extractAuthentication(Map<String, ?> map) {
  2. //检查map中包不包含user_name
  3. if (map.containsKey("user_name")) {
  4. Object principal = map.get("user_name");
  5. Collection<? extends GrantedAuthority> authorities = this.getAuthorities(map);
  6. if (this.userDetailsService != null) {
  7. UserDetails user = this.userDetailsService.loadUserByUsername((String)map.get("user_name"));
  8. authorities = user.getAuthorities();
  9. principal = user;
  10. }
  11. return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
  12. } else {
  13. return null;
  14. }
  15. }
  1. private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
  2. if (!map.containsKey("authorities")) {
  3. return this.defaultAuthorities;
  4. } else {
  5. Object authorities = map.get("authorities");
  6. if (authorities instanceof String) {
  7. return AuthorityUtils.commaSeparatedStringToAuthorityList((String)authorities);
  8. } else if (authorities instanceof Collection) {
  9. return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.collectionToCommaDelimitedString((Collection)authorities));
  10. } else {
  11. throw new IllegalArgumentException("Authorities must be either a String or a Collection");
  12. }
  13. }
  14. }

回到DefaultAccessTokenConverter

注意绿字属性名,这里代表有些属性名是固定的。

注意红框部分。这里代表了jwt Token 没有带user_name 也可以,只要jwt Token中包含了authorities属性,然后将Authentication和OAuth2Request封装为OAuth2Authentication依次传回到DefaultTokenServices

再次传回DefaultTokenServices

这里也需要注意。这个类的ClientDetailsService也是可以配置的,配置后可以进行检验ClientId是否合法

继续传回

传回OAuth2AuthenticationManager

传回OAuth2AuthenticationProcessingFilter

最后将用户认证信息设置到SecurityContext中

jwt校验总结

通过设置请求头方式请求资源服务器,会被OAuth2的OAuth2AuthenticationProcessingFilter过滤器所拦截,通过资源服务配置文件中指定的JwtTokenStore和JwtAccessTokenConverter,使用配置好的公钥进行解析jwt Token得到一系列信息。这里需要注意,如user_name,authorities等属性名已经固定。所以需要在token生成的时候就注意名字规范。最后将封装好的Authorication设置到SecurityContext中。

@PreAuthorize中hasAuthority怎么获取权限的?

hasAuthority为SecurityExpressionRoot的方法,此方法会从Authorication中获取authorities集合,并检查hasAuthority中要求的权限是否在集合中。满足要求则执行,否则抛出异常。

 

SecurityExpressionRoot怎么生成的?

经过路径查看可以看出通过Aop的方式在

的beforeInvocation方法传入Authorication一路传递参数,最后初始化SecurityExpressionRoot类

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/765894
推荐阅读
相关标签
  

闽ICP备14008679号