使用Spring Security从JWT令牌中提取当前登录的用户信息 [英] Extract Currently Logged in User information from JWT token using Spring Security
问题描述
我已经使用Spring Security Oauth2实现了JWT和LDAP身份验证.似乎工作正常,我可以使用LDAP凭据登录.
I have implemented JWT and LDAP Authentication using Spring Security Oauth2. It seems to be working fine and I can login with my LDAP credentials.
现在,有一个要求,我需要使用当前登录的用户信息将详细信息保存在数据库中-特别是当该用户添加/更新新记录时.我试图通过
Now, there is one requirement that I need to use the currently logged in user info to save details in database - specifically like when that user add/update a new record. I tried to get that using Spring security way using
SecurityContextHolder.getContext().getAuthentication().getDetails()
但是它不会返回我在JWT中拥有的所有信息.它仅返回远程IP,JWT令牌值和经过验证的true.它甚至不返回name().
but it doesn't return all that information which I have in JWT. It just returns Remote IP,the JWT token value and authenticated true. It doesn't even return name().
我是JWT的新手,所以不确定是否需要通过读取该令牌以及我们如何实现该令牌来提取它.
I am new to JWT, so not sure if I need to extract it by reading that token and even how we can achieve it.
任何指针都会受到赞赏.
Any pointers will be appreciated.
谢谢.
推荐答案
要做的第一件事是在创建JWT时将用户信息存储在JWT中,然后在使用它时必须将其提取.我遇到了类似的情况,我通过扩展TokenEnhancer
和JwtAccessTokenConverter
来解决了这个问题.
The first thing you need to do is store the user information inside the JWT when it is created, then you have to extract it when it is used. I had a similar situation and I solved it by extending both the TokenEnhancer
and JwtAccessTokenConverter
.
我使用TokenEnhancer
将类型为CustomUserDetails
的扩展主体嵌入JWT其他信息中.
I use the TokenEnhancer
to embed my extended principal of type CustomUserDetails
inside the JWT additional information.
public class CustomAccessTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Authentication userAuthentication = authentication.getUserAuthentication();
if (userAuthentication != null) {
Object principal = authentication.getUserAuthentication().getPrincipal();
if (principal instanceof CustomUserDetails) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("userDetails", principal);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
}
return accessToken;
}
}
然后在处理经过身份验证的请求时在构建Authentication
对象时手动提取扩展的主体.
And then manually extract the extended principal when building the Authentication
object when processing an authenticated request.
public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication authentication = super.extractAuthentication(map);
Authentication userAuthentication = authentication.getUserAuthentication();
if (userAuthentication != null) {
LinkedHashMap userDetails = (LinkedHashMap) map.get("userDetails");
if (userDetails != null) {
// build your principal here
String localUserTableField = (String) userDetails.get("localUserTableField");
CustomUserDetails extendedPrincipal = new CustomUserDetails(localUserTableField);
Collection<? extends GrantedAuthority> authorities = userAuthentication.getAuthorities();
userAuthentication = new UsernamePasswordAuthenticationToken(extendedPrincipal,
userAuthentication.getCredentials(), authorities);
}
}
return new OAuth2Authentication(authentication.getOAuth2Request(), userAuthentication);
}
}
和AuthorizationServer
配置将它们绑在一起.
and the AuthorizationServer
configuration to tie it all together.
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
CustomJwtAccessTokenConverter accessTokenConverter = new CustomJwtAccessTokenConverter();
accessTokenConverter.setSigningKey("a1b2c3d4e5f6g");
return accessTokenConverter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomAccessTokenEnhancer();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.passwordEncoder(passwordEncoder());
security.checkTokenAccess("isAuthenticated()");
}
}
然后我可以像这样在资源控制器中访问扩展的主体
I am then able to access my extended principal in my resource controller like this
@RestController
public class SomeResourceController {
@RequestMapping("/some-resource")
public ResponseEntity<?> someResource(Authentication authentication) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return ResponseEntity.ok("woo hoo!");
}
}
希望这会有所帮助!
这篇关于使用Spring Security从JWT令牌中提取当前登录的用户信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!