使用 Spring 安全性从 JWT 令牌中提取当前登录的用户信息
Extract Currently Logged in User information from JWT token using Spring Security
我已经使用 Spring Security Oauth2 实现了 JWT 和 LDAP 身份验证。它似乎工作正常,我可以使用我的 LDAP 凭据登录。
现在,我需要使用当前登录的用户信息在数据库中保存详细信息 - 特别是当该用户 add/update 一条新记录时。我尝试使用
使用 Spring 安全方式来获取它
SecurityContextHolder.getContext().getAuthentication().getDetails()
但它没有 return 我在 JWT 中拥有的所有信息。它只是 returns 远程 IP、JWT 令牌值和经过身份验证的 true。它甚至没有 return name().
我是 JWT 的新手,所以不确定是否需要通过读取该令牌来提取它,甚至不确定我们如何实现它。
任何指点将不胜感激。
谢谢。
您需要做的第一件事是在创建 JWT 时将用户信息存储在 JWT 中,然后在使用时将其提取出来。我遇到了类似的情况,我通过扩展 TokenEnhancer
和 JwtAccessTokenConverter
.
解决了这个问题
我使用 TokenEnhancer
在 JWT 附加信息中嵌入类型为 CustomUserDetails
的扩展主体。
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
对象时手动提取扩展主体。
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
配置将它们结合在一起。
@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()");
}
}
然后我可以像这样在我的资源控制器中访问我的扩展主体
@RestController
public class SomeResourceController {
@RequestMapping("/some-resource")
public ResponseEntity<?> someResource(Authentication authentication) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return ResponseEntity.ok("woo hoo!");
}
}
希望对您有所帮助!
在您的 REST 服务中,添加 OAuth2Authentication Class 作为参数
@RequestMapping(value = "/{id}/products", method = RequestMethod.POST)
public ResourceResponse<String> processProducts(OAuth2Authentication auth) {
Springboot 会自动将 logged-in 用户详细信息映射到此对象。现在,您可以执行以下操作来访问用户名
auth.getPrincipal().toString()
我已经使用 Spring Security Oauth2 实现了 JWT 和 LDAP 身份验证。它似乎工作正常,我可以使用我的 LDAP 凭据登录。
现在,我需要使用当前登录的用户信息在数据库中保存详细信息 - 特别是当该用户 add/update 一条新记录时。我尝试使用
使用 Spring 安全方式来获取它SecurityContextHolder.getContext().getAuthentication().getDetails()
但它没有 return 我在 JWT 中拥有的所有信息。它只是 returns 远程 IP、JWT 令牌值和经过身份验证的 true。它甚至没有 return name().
我是 JWT 的新手,所以不确定是否需要通过读取该令牌来提取它,甚至不确定我们如何实现它。
任何指点将不胜感激。
谢谢。
您需要做的第一件事是在创建 JWT 时将用户信息存储在 JWT 中,然后在使用时将其提取出来。我遇到了类似的情况,我通过扩展 TokenEnhancer
和 JwtAccessTokenConverter
.
我使用 TokenEnhancer
在 JWT 附加信息中嵌入类型为 CustomUserDetails
的扩展主体。
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
对象时手动提取扩展主体。
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
配置将它们结合在一起。
@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()");
}
}
然后我可以像这样在我的资源控制器中访问我的扩展主体
@RestController
public class SomeResourceController {
@RequestMapping("/some-resource")
public ResponseEntity<?> someResource(Authentication authentication) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return ResponseEntity.ok("woo hoo!");
}
}
希望对您有所帮助!
在您的 REST 服务中,添加 OAuth2Authentication Class 作为参数
@RequestMapping(value = "/{id}/products", method = RequestMethod.POST)
public ResourceResponse<String> processProducts(OAuth2Authentication auth) {
Springboot 会自动将 logged-in 用户详细信息映射到此对象。现在,您可以执行以下操作来访问用户名
auth.getPrincipal().toString()