使用 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.
现在,我需要使用当前登录的用户信息将详细信息保存在数据库中 - 特别是当该用户添加/更新新记录时.我尝试使用 Spring 安全方式使用
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.
任何指针将不胜感激.
谢谢.
推荐答案
你需要做的第一件事就是在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屋!