最近用spring security + oauth2 做认证和授权,碰到个非常懊恼的问题,从安全上下文获取用户信息,永远只给你返回个用户名。因为在很多地方,要用到用户id, 不可能又用用户名去数据库查id吧,这就非常蛋腾了。
百度了人民群众的意见,折腾了老半天,终于把它搞掂了。
源码就不贴了, 这贴修改的,供各位参考参考:
一 、 重写DefaultUserAuthenticationConverter里面的convertUserAuthentication方法:
@Component @Slf4j public class SetAdditionalInfoToTokenConverter extends DefaultUserAuthenticationConverter { @Override public MapconvertUserAuthentication(Authentication authentication) { Map response = new linkedHashMap (); // response.put(USERNAME, authentication.getName()); 源码里面的,不知道USERNAME是什么,注释掉吧 Object principal = authentication.getPrincipal(); //不要强制转换principal, 用json转化,不然各种莫名其妙的坑 String jsonStr=JSON.toJSonString(principal); JSonObject jsonObject=JSON.parseObject(jsonStr); //加上自定义的用户名和id long id=jsonObject.getLong("id"); response.put("id", id); String username=jsonObject.getString("username"); response.put("username",username); if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) { response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities())); } return response; } //懒得写, 从源码复制过来的 private Collection extends GrantedAuthority> getAuthorities(Map map) { if (!map.containsKey(AUTHORITIES)) { return List.of(); } 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)); } throw new IllegalArgumentException("Authorities must be either a String or a Collection"); } }
二,把这个重写的类 SetAdditionalInfoToTokenConverter 设置到授权服务器配置类里面 :
SetAdditionalInfoToTokenConverter ----> defaultAccessTokenConverter ->jwtAccessTokenConverter--->endpints.tokenEnhancer()
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Qualifier("customUserDetailService") @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Autowired SetAdditionalInfoToTokenConverter setAdditionalInfoToTokenConverter; //定制保存在jwt里面的信息 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .accessTokenConverter(jwtAccessTokenConverter()) .tokenStore(jwtTokenStore()) .tokenEnhancer(jwtAccessTokenConverter()); super.configure(endpoints); } public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setAccessTokenConverter(accessTokenConverter()); return jwtAccessTokenConverter; } @Bean public DefaultAccessTokenConverter accessTokenConverter(){ DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter(); defaultAccessTokenConverter.setUserTokenConverter(setAdditionalInfoToTokenConverter); return defaultAccessTokenConverter; } }
三, 上面两步只是把额外信息(用户id,用户名)设置进jwtoken, 还不能用get principal获取,我们再继续重写 DefaultUserAuthenticationConverte 里面的 extractAuthentication方法,这个方法实现怎样提取信息到anthentication。因为后面要把重写的类设置进资源服务器配置类里面,所以重新创建一个component: ExtractAuthenticationConverter
@Component @Slf4j public class ExtractAuthenticationConverter extends DefaultUserAuthenticationConverter { @Override public Authentication extractAuthentication(Mapmap) { String username="username"; String id="id"; Map principal=new HashMap<>(); if(map.containsKey(username)){ principal.put(username, map.get(username)); } if(map.containsKey(id)){ principal.put(id, map.get(id)); } Collection extends GrantedAuthority> authorities = getAuthorities(map); return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities); } //从源码复制过来的 private Collection extends GrantedAuthority> getAuthorities(Map map) { if (!map.containsKey(AUTHORITIES)) { return List.of(); } 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)); } throw new IllegalArgumentException("Authorities must be either a String or a Collection"); } }
四、 把重写了 extractAuthentication的类 extractAuthenticationConverter 设置进资源服务器配置类:
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired ExtractAuthenticationConverter extractAuthenticationConverter; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenStore(jwtTokenStore()); } private TokenStore jwtTokenStore() { JwtTokenStore jwtTokenStore = new JwtTokenStore(accessTokenConverter()); return jwtTokenStore; } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter(); //如果用了公私钥,在这里解析公钥。。。 省略 // 新建一个defaultAccessTokenConverter中介把 自定义的extractAuthenticationConverter设置进去 DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter(); defaultAccessTokenConverter.setUserTokenConverter(extractAuthenticationConverter); tokenConverter.setAccessTokenConverter(defaultAccessTokenConverter); return tokenConverter; } }
至此,全部设置完,建个接口测试一下:
@GetMapping("/principle") public Object getUserInfo(){ return SecurityContextHolder.getContext().getAuthentication().getPrincipal(); }
返回 :
{
"id": 12312154321,
"username": "admin"
}
ok,成功。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)