我正在尝试使用Springboot 3和SpringSecurity 6来设置OAuth2 Resource Server。
我能够正确配置身份验证;我实现了一个自定义的jwt转换器,它从我的数据库中获取用户的权限,并将它们分配给安全上下文。
class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
AzureUserService azureUserService;
public CustomAuthenticationConverter(AzureUserService azureUserService) {
this.azureUserService = azureUserService;
}
public AbstractAuthenticationToken convert(@NonNull Jwt jwt) {
// Currently, creates a user in the database, if they don't exist yet. Guaranteed to get a user.
User user = azureUserService.retrieveOrCreateUserFromJwt(jwt);
JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt, user.getAuthorities(), user.getId());
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}
}
字符串
现在我可以用权威来保护我的路线了。下一步:我希望能够访问一些路由内部的有关当前用户的信息。然而,即使在注册了我的用户详细信息服务类之后:
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
return provider;
}
型
我无法访问路由中的UserDetails对象:
@GetMapping("/api/admin/you")
public String showProfile(@AuthenticationPrincipal CustomUserDetails userDetails) {
return "Details"+ userDetails;
}
型
这将返回“Details null”。
我不确定UserDetails是否不可访问,因为我只是将此应用程序用作OAuthResource Server,或者我没有正确配置它。
我的UserDetails类的实现:
public class CustomUserDetails implements UserDetails {
private final User user; // This is your own User entity class
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
// We use OAuth2, hence no need for password
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return user.getId();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return user.getEnabled();
}
public User getUser() {
return user;
}
}
型
以及服务:
@Transactional
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Note that Spring calls the unique user identifier "username", but we're working with a unique identifier.
@Override
public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> optionalUser = userRepository.findById(username);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
return new CustomUserDetails(user);
}
else {
throw new UsernameNotFoundException("User not found with id: " + username);
}
}
}
型
另外,这里是我的SecurityConfiguration
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Create RequestMatchers out of the Strings
List<RequestMatcher> publicMatchers = Arrays.stream(publicPaths)
.map(AntPathRequestMatcher::new)
.collect(Collectors.toList());
// Apply configuration
http
.authenticationProvider(authenticationProvider())
.authorizeHttpRequests(authorizeRequests -> {
authorizeRequests
// Allow public access to the routes specified in 'publicMatchers' list
.requestMatchers(request ->
publicMatchers.stream().anyMatch(matcher -> matcher.matches(request))
).permitAll()
// Require authentication for all other routes.
.anyRequest().authenticated();
})
// Configure the OAuth 2.0 resource server to use JWT tokens provided by Azure AD B2C
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer
.jwt(jwt ->
// Set the location where the server can find the JSON Web Key Set (JWK Set).
// The JWK Set contains public keys. The server uses these public keys to
// validate the signatures of incoming JWTs, ensuring they were issued by a trusted authorization server.
// In this case, the URI of the JWK Set is provided by the 'jwkSetUri' variable.
jwt.jwkSetUri(this.jwkSetUri)
.jwtAuthenticationConverter(customAuthenticationConverter())
)
)
.userDetailsService(userDetailsService);
return http.build();
}
型
1条答案
按热度按时间du7egjpx1#
在认证转换器bean中连接JPA存储库是可行的(例如,我做的是here),但是这是一个不好的实践,应该保留到不能在授权服务器上设置访问令牌私有声明的情况下,并且不能在它前面使用另一个(更可配置的)授权服务器。在Keycloak中,您使用“mappers”来完成,在Auth0中,您使用“actions”来完成,在Azure中,您使用“API connectors”来完成。
每次令牌发布时从授权服务器上的DB获取角色一次效率要高得多,而不是针对资源服务器上的每个传入请求获取角色。您可以使用缓存来缓解这种情况,但是创建、维护和调试缓存(分布式环境中的共享缓存)总是一件痛苦的事情。
使用授权转换器从DB获取角色的工作解决方案的细节(但是,如果你这样做,我打赌你迟早会后悔的):
字符串