Spring Security Oauth2を使用してJWTおよびLDAP認証を実装しました。正常に機能しているようで、LDAP資格情報でログインできます。
ここで、現在ログインしているユーザー情報を使用してデータベースに詳細を保存する必要があるという要件が1つあります。特に、そのユーザーが新しいレコードを追加/更新するときなどです。私はSpringセキュリティ方法を使用してそれを取得しようとしました
SecurityContextHolder.getContext().getAuthentication().getDetails()
しかし、JWTにあるすべての情報を返すわけではありません。リモートIP、JWTトークン値、認証済みtrueを返すだけです。 name()さえも返しません。
私はJWTが初めてなので、そのトークンを読み取ることでそれを抽出する必要があるかどうか、またどのようにそれを達成できるかさえもわかりません。
すべてのポインタが高く評価されます。
ありがとう。
最初に行う必要があるのは、JWTが作成されたときにJWT内にユーザー情報を保存し、使用時にそれを抽出する必要があることです。同様の状況があり、TokenEnhancer
とJwtAccessTokenConverter
の両方を拡張することで解決しました。
JWT追加情報内にTokenEnhancer
型を使用して、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クラスを引数として追加します
@RequestMapping(value = "/{id}/products", method = RequestMethod.POST)
public ResourceResponse<String> processProducts(OAuth2Authentication auth) {
Springbootは、ログインしたユーザーの詳細をこのオブジェクトに自動的にマッピングします。これで、以下を実行してユーザー名にアクセスできます
auth.getPrincipal().toString()