如何在 Spring 授权服务器 0.2.0 中实现自定义 UserDetailsService 或自定义 AuthenticationProvider
How to implement custom UserDetailsService or custom AuthenticationProvider in Spring authorization server 0.2.0
我正在尝试新的 Spring 授权服务器 0.2.0。我已成功 运行 位于 https://github.com/spring-projects/spring-authorization-server/tree/main/samples/boot/oauth2-integration.
的示例应用程序
现在,我正在尝试将自定义 UserDetailsService 添加到授权服务器。我创建了一个自定义的 UserDetailsService,用户保存在 Mysql 数据库中。
我已经替换了这个
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
有了这个
@Bean
UserDetailsService users() {
return new CustomUserDetailsService();
}
现在,应用程序在尝试登录“授权码”授权流程时抛出错误“找不到 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 的 AuthenticationProvider”。
我不知道在哪里添加我的自定义 AuthenticationProvider。我尝试如下添加到 DefaultSecurityConfig。但是授权码授予流程总是returns invalid_client.
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authenticationProvider(authenticationProvider)
.authorizeRequests(authorizeRequests ->
authorizeRequests.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
我想,我在这里遗漏了一些关于客户端身份验证的内容,因为我的自定义 authenticationProvider 必须仅为用户而不是客户端验证该方法。
现在我的问题是如何在不将 AuthenticationProvider 添加到 Spring 授权服务器的情况下添加自定义 AuthenticationProvider 或 CustomUserDetailsService。
更新:
这是我的 CustomUserDetailsService 和 CustomAuthenticationProvider
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public DcubeUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Supplier<UsernameNotFoundException> s =
() -> new UsernameNotFoundException("Problem during authentication!");
DcubeUser u = userRepository.findUserByUsername(username).orElseThrow(s);
return new DcubeUserDetails(u);
}
}
@Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
CustomUserDetails user = userDetailsService.loadUserByUsername(username);
return checkPassword(user, password);
}
@Override
public boolean supports(Class<?> aClass) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
}
private Authentication checkPassword(CustomUserDetails user, String rawPassword) {
if (Objects.equals(rawPassword, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
} else {
throw new BadCredentialsException("Bad credentials");
}
}
}
这是我的日志
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.s.HttpSessionRequestCache : Did not match request /error to the saved one DefaultSavedRequest [http://localhost:8888/oauth2/authorize?response_type=code&client_id=web-client&scope=web:write]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (10/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (11/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (12/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (13/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (14/14)
2021-10-27 09:08:34.387 DEBUG 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Secured GET /error
2021-10-27 09:08:34.464 DEBUG 10928 --- [nio-8888-exec-7] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter@42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter@55e88bc, org.springframework.security.web.csrf.CsrfFilter@20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter@67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@166a5659, org.springframework.security.web.session.SessionManagementFilter@4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter@417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@1bcf2c64]] (1/2)
2021-10-27 09:08:56.440 DEBUG 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Securing POST /oauth2/authorize
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]] from SPRING_SECURITY_CONTEXT
2021-10-27 09:08:56.441 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]]
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65]]]
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:08:56.444 TRACE 10928 --- [nio-8888-exec-8] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2AuthorizationCodeRequestAuthenticationProvider (1/8)
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/authorized?code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e
2021-10-27 09:08:56.447 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:09:23.121 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter@42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter@55e88bc, org.springframework.security.web.csrf.CsrfFilter@20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter@67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@166a5659, org.springframework.security.web.session.SessionManagementFilter@4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter@417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@1bcf2c64]] (1/2)
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Securing POST /oauth2/token?grant_type=authorization_code&code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e&scope=dcube:write&redirect_uri=http://localhost:8888/authorized
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65]]]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OidcProviderConfigurationEndpointFilter (7/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking NimbusJwkSetEndpointFilter (8/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationServerMetadataEndpointFilter (9/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2ClientAuthenticationFilter (10/22)
2021-10-27 09:09:23.124 TRACE 10928 --- [io-8888-exec-10] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2ClientAuthenticationProvider (1/8)
2021-10-27 09:09:23.124 WARN 10928 --- [io-8888-exec-10] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
2021-10-27 09:09:23.127 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
首先,交叉检查您是否在安全配置文件中进行了相关更改。
@Configuration
@EnableWebSecurity
@ComponentScan("com.frugalis")
public class CustAuthProviderConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
以上代码注册自定义身份验证提供程序并授权用户。
要创建自定义用户服务,您需要实现UserDetailsService
接口并覆盖loadUserByUsername()
方法。
在 service
包下创建 UserDetailsServiceImp
class。
public class UserDetailsServiceImp implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here we are using dummy data, you need to load user data from
database or other third party application*/
User user = findUserbyUername(username);
UserBuilder builder = null;
if (user != null) {
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
builder.roles(user.getRoles());
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}
private User findUserbyUername(String username) {
if(username.equalsIgnoreCase("admin")) {
return new User(username, "admin123", "ADMIN");
}
return null;
}
}
确保使用合适的 spring 引导版本和 maven 存储库。
更多请参考这个例子:Spring Security 5 - Custom UserDetailsService example
现在定义一个自定义身份验证提供程序。
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
boolean shouldAuthenticateAgainstThirdPartySystem = true;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (name.equals("admin") && password.equals("password")) {
final List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
final UserDetails principal = new User(name, password, grantedAuths);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
return auth;
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
在上面的代码中,我们从身份验证对象中检索了用户名和密码。一旦我们检索到身份验证对象和凭据,我们就会验证用户名和密码。
您可以执行基于数据库的身份验证,我们在这里进行了硬编码验证。
一旦用户有效,我们就尝试在字符串列表中设置 GRANTED_AUTHORITY
和 return 一个 UserDetails 对象给调用者一个身份验证对象。我们可以自定义 principal 和 return.
中设置的用户对象,而不是 spring 提供的 UserDetails
您需要从 DefaultSecurityConfig
class 中删除以下 Bean
。
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
将以下方法和 Autowired
自定义 AuthenticationProvider
添加到相同的 class
@Autowired
public void myCoolMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
现在一切如我所愿。
完整的DefaultSecurityConfig
会像
@EnableWebSecurity
public class DefaultSecurityConfig {
@Autowired
private CustomAuthenticationProvider authenticationProvider;
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
@Autowired
public void whateverMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
我正在尝试新的 Spring 授权服务器 0.2.0。我已成功 运行 位于 https://github.com/spring-projects/spring-authorization-server/tree/main/samples/boot/oauth2-integration.
的示例应用程序现在,我正在尝试将自定义 UserDetailsService 添加到授权服务器。我创建了一个自定义的 UserDetailsService,用户保存在 Mysql 数据库中。
我已经替换了这个
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
有了这个
@Bean
UserDetailsService users() {
return new CustomUserDetailsService();
}
现在,应用程序在尝试登录“授权码”授权流程时抛出错误“找不到 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 的 AuthenticationProvider”。
我不知道在哪里添加我的自定义 AuthenticationProvider。我尝试如下添加到 DefaultSecurityConfig。但是授权码授予流程总是returns invalid_client.
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authenticationProvider(authenticationProvider)
.authorizeRequests(authorizeRequests ->
authorizeRequests.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
我想,我在这里遗漏了一些关于客户端身份验证的内容,因为我的自定义 authenticationProvider 必须仅为用户而不是客户端验证该方法。
现在我的问题是如何在不将 AuthenticationProvider 添加到 Spring 授权服务器的情况下添加自定义 AuthenticationProvider 或 CustomUserDetailsService。
更新:
这是我的 CustomUserDetailsService 和 CustomAuthenticationProvider
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public DcubeUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Supplier<UsernameNotFoundException> s =
() -> new UsernameNotFoundException("Problem during authentication!");
DcubeUser u = userRepository.findUserByUsername(username).orElseThrow(s);
return new DcubeUserDetails(u);
}
}
@Service
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
CustomUserDetails user = userDetailsService.loadUserByUsername(username);
return checkPassword(user, password);
}
@Override
public boolean supports(Class<?> aClass) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
}
private Authentication checkPassword(CustomUserDetails user, String rawPassword) {
if (Objects.equals(rawPassword, user.getPassword())) {
return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
} else {
throw new BadCredentialsException("Bad credentials");
}
}
}
这是我的日志
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.s.HttpSessionRequestCache : Did not match request /error to the saved one DefaultSavedRequest [http://localhost:8888/oauth2/authorize?response_type=code&client_id=web-client&scope=web:write]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderAwareRequestFilter (10/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking AnonymousAuthenticationFilter (11/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter : Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking SessionManagementFilter (12/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking ExceptionTranslationFilter (13/14)
2021-10-27 09:08:34.387 TRACE 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Invoking FilterSecurityInterceptor (14/14)
2021-10-27 09:08:34.387 DEBUG 10928 --- [nio-8888-exec-7] o.s.security.web.FilterChainProxy : Secured GET /error
2021-10-27 09:08:34.464 DEBUG 10928 --- [nio-8888-exec-7] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter@42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter@55e88bc, org.springframework.security.web.csrf.CsrfFilter@20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter@67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@166a5659, org.springframework.security.web.session.SessionManagementFilter@4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter@417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@1bcf2c64]] (1/2)
2021-10-27 09:08:56.440 DEBUG 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Securing POST /oauth2/authorize
2021-10-27 09:08:56.440 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]] from SPRING_SECURITY_CONTEXT
2021-10-27 09:08:56.441 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=Ramesh, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=D4FF0763B895353AC334140528DE35CD], Granted Authorities=[ADMIN]]]
2021-10-27 09:08:56.441 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65]]]
2021-10-27 09:08:56.442 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:08:56.443 TRACE 10928 --- [nio-8888-exec-8] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:08:56.444 TRACE 10928 --- [nio-8888-exec-8] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2AuthorizationCodeRequestAuthenticationProvider (1/8)
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/authorized?code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e
2021-10-27 09:08:56.447 TRACE 10928 --- [nio-8888-exec-8] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:08:56.447 DEBUG 10928 --- [nio-8888-exec-8] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2021-10-27 09:09:23.121 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@421d7900, org.springframework.security.web.context.SecurityContextPersistenceFilter@42a7e7e1, org.springframework.security.web.header.HeaderWriterFilter@55e88bc, org.springframework.security.web.csrf.CsrfFilter@20a116a0, org.springframework.security.web.authentication.logout.LogoutFilter@67e21ea2, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter@e2ee348, org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter@5d67bf4d, org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter@1477d4e6, org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter@5ec3689b, org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter@448fa659, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c0a6f62, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4946dfde, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@45964b9e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@554e9509, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@34ea86ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@166a5659, org.springframework.security.web.session.SessionManagementFilter@4c12f54a, org.springframework.security.web.access.ExceptionTranslationFilter@417b3642, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@22ed2886, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter@76219fe, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter@4c599679, org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter@1bcf2c64]] (1/2)
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Securing POST /oauth2/token?grant_type=authorization_code&code=iaFZSqcRLeJucw2mx_HNgji1PWN9QPHaUZt0htdH2zc3_4hEPFoBamnijwuRcK2xTzOT_W4jCTne3AmjAKB2gyoVzod5otPfgB8WSLc_8-x2B13oapwhlWX4dBUUER2e&scope=dcube:write&redirect_uri=http://localhost:8888/authorized
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (1/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking SecurityContextPersistenceFilter (2/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Created SecurityContextImpl [Null authentication]
2021-10-27 09:09:23.122 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (3/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (4/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match And [CsrfNotRequired [TRACE, HEAD, GET, OPTIONS], Not [Or [org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer$$Lambda85/0x0000000801355c90@32e7df65]]]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (5/22)
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2021-10-27 09:09:23.122 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationEndpointFilter (6/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OidcProviderConfigurationEndpointFilter (7/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking NimbusJwkSetEndpointFilter (8/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2AuthorizationServerMetadataEndpointFilter (9/22)
2021-10-27 09:09:23.123 TRACE 10928 --- [io-8888-exec-10] o.s.security.web.FilterChainProxy : Invoking OAuth2ClientAuthenticationFilter (10/22)
2021-10-27 09:09:23.124 TRACE 10928 --- [io-8888-exec-10] o.s.s.authentication.ProviderManager : Authenticating request with OAuth2ClientAuthenticationProvider (1/8)
2021-10-27 09:09:23.124 WARN 10928 --- [io-8888-exec-10] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
2021-10-27 09:09:23.127 TRACE 10928 --- [io-8888-exec-10] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2021-10-27 09:09:23.127 DEBUG 10928 --- [io-8888-exec-10] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
首先,交叉检查您是否在安全配置文件中进行了相关更改。
@Configuration
@EnableWebSecurity
@ComponentScan("com.frugalis")
public class CustAuthProviderConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider authProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
以上代码注册自定义身份验证提供程序并授权用户。
要创建自定义用户服务,您需要实现UserDetailsService
接口并覆盖loadUserByUsername()
方法。
在 service
包下创建 UserDetailsServiceImp
class。
public class UserDetailsServiceImp implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here we are using dummy data, you need to load user data from
database or other third party application*/
User user = findUserbyUername(username);
UserBuilder builder = null;
if (user != null) {
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.password(new BCryptPasswordEncoder().encode(user.getPassword()));
builder.roles(user.getRoles());
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}
private User findUserbyUername(String username) {
if(username.equalsIgnoreCase("admin")) {
return new User(username, "admin123", "ADMIN");
}
return null;
}
}
确保使用合适的 spring 引导版本和 maven 存储库。
更多请参考这个例子:Spring Security 5 - Custom UserDetailsService example
现在定义一个自定义身份验证提供程序。
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
boolean shouldAuthenticateAgainstThirdPartySystem = true;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (name.equals("admin") && password.equals("password")) {
final List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
final UserDetails principal = new User(name, password, grantedAuths);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
return auth;
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
在上面的代码中,我们从身份验证对象中检索了用户名和密码。一旦我们检索到身份验证对象和凭据,我们就会验证用户名和密码。
您可以执行基于数据库的身份验证,我们在这里进行了硬编码验证。
一旦用户有效,我们就尝试在字符串列表中设置 GRANTED_AUTHORITY
和 return 一个 UserDetails 对象给调用者一个身份验证对象。我们可以自定义 principal 和 return.
您需要从 DefaultSecurityConfig
class 中删除以下 Bean
。
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
将以下方法和 Autowired
自定义 AuthenticationProvider
添加到相同的 class
@Autowired
public void myCoolMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
现在一切如我所愿。
完整的DefaultSecurityConfig
会像
@EnableWebSecurity
public class DefaultSecurityConfig {
@Autowired
private CustomAuthenticationProvider authenticationProvider;
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated())
.formLogin(withDefaults());
return http.build();
}
@Autowired
public void whateverMethodName(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}