带有自定义身份验证提供程序的 OAUTH2 用户服务
OAUTH2 user service with Custom Authentication Providers
我是 Spring 安全和 Oauth2 的新手。在我的 spring 引导应用程序中,我通过以下一组更改实现了 Oauth2 身份验证:
自定义Ouath2用户服务如下:
@Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
...
}
}
安全配置如下:
@EnableWebSecurity
@Import(SecurityProblemSupport.class)
@ConditionalOnProperty(
value = "myapp.authentication.type",
havingValue = "oauth",
matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) {
this.customOAuth2UserService = customOAuth2UserService;
}
@Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bundle.js")
.antMatchers("/slds-icons/**")
.antMatchers("/assets/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/swagger-resources")
.antMatchers("/v2/api-docs")
.antMatchers("/api/redirectToHome")
.antMatchers("/test/**");
}
public void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/api/", null);
@Override
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
};
http.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()//.and().oauth2ResourceServer().jwt()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2**")
.and()
.failureUrl("/api/redirectToHome")
.userInfoEndpoint().userService(oauth2UserService())
;
http.cors().disable();
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return customOAuth2UserService;
}
}
application.properties内容如下:
spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://internal.authprovider.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name
myapp.authentication.type=oauth
现在,对于现有的身份验证机制,我想添加对多个身份验证提供程序的支持:LDAP、Form-Login 等
关于这方面,我看过几篇文章:
- https://www.baeldung.com/spring-security-multiple-auth-providers
- Custom Authentication provider with Spring Security and Java Config
但是,对于我应该对现有代码库进行哪些更改以实现此目的,我没有任何具体的想法。
有人可以帮忙吗?谢谢。
我从您的代码开始创建了一个简化的设置,同时支持 OAuth2 和 Basic Auth。
/tenant2/**
将启动基本身份验证。
/**
(其他所有内容)触发 OAuth2 授权码身份验证。
实现这一点的关键是每个身份验证类型有一个 @Configuration
class。
让我们从控制器开始:
Tenant1HomeController
@Controller
public class Tenant1HomeController {
@GetMapping("/tenant1/home")
public String home() {
return "tenant1Home";
}
}
Tenant2HomeController
@Controller
public class Tenant2HomeController {
@GetMapping("/tenant2/home")
public String home() {
return "tenant2Home";
}
}
现在,配置 classes:
Tenant1SecurityConfiguration
@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority("ADMIN")
.antMatchers("/tenant1/**").authenticated()
.and()
.oauth2Login()
.and()
.cors()
.disable();
}
}
Tenant2SecurityConfiguration(注意 @Order(90)
,这很重要
@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/tenant2/**").hasAuthority("BASIC_USER")
.and()
.httpBasic();
http.cors().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("BASIC_USER");
}
}
最后配置:
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: myclient
client-secret: c6dce03e-ea13-4b76-8aab-c876f5c2c1d9
provider:
keycloak:
issuer-uri: http://localhost:8180/auth/realms/myrealm
有了这个,如果我们点击 http://localhost:8080/tenant2/home
,将提示基本身份验证弹出窗口:
尝试 http://localhost:8080/tenant1/home
会将您带到 Keycloak 的登录表单:
更新:
用上面的配置配置一个多租户应用是完全可行的。
关键是每个身份验证提供商都与一组不同的用户(租户)合作,例如:
租户 1(OAuth2 身份验证):
@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
...
.and()
.oauth2Login()
.and()
...
第一个用户子集由 OAuth2 提供商联合,在本例中为 Keycloak。
租户 2(基本/表单/xxx 身份验证):
@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
...
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(XXX)
对于第二个租户,您可以使用指向不同用户存储库(LDAP、数据库...)的 userDetailsService。
我是 Spring 安全和 Oauth2 的新手。在我的 spring 引导应用程序中,我通过以下一组更改实现了 Oauth2 身份验证:
自定义Ouath2用户服务如下:
@Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
...
}
}
安全配置如下:
@EnableWebSecurity
@Import(SecurityProblemSupport.class)
@ConditionalOnProperty(
value = "myapp.authentication.type",
havingValue = "oauth",
matchIfMissing = true
)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;
public SecurityConfiguration(CustomOAuth2UserService customOAuth2UserService) {
this.customOAuth2UserService = customOAuth2UserService;
}
@Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bundle.js")
.antMatchers("/slds-icons/**")
.antMatchers("/assets/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/swagger-resources")
.antMatchers("/v2/api-docs")
.antMatchers("/api/redirectToHome")
.antMatchers("/test/**");
}
public void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/api/", null);
@Override
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
};
http.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()//.and().oauth2ResourceServer().jwt()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/oauth2**")
.and()
.failureUrl("/api/redirectToHome")
.userInfoEndpoint().userService(oauth2UserService())
;
http.cors().disable();
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return customOAuth2UserService;
}
}
application.properties内容如下:
spring.security.oauth2.client.registration.keycloak.client-id=abcd
spring.security.oauth2.client.registration.keycloak.client-name=Auth Server
spring.security.oauth2.client.registration.keycloak.scope=api
spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-authentication-method=basic
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
myapp.oauth2.path=https://internal.authprovider.com/oauth2/
spring.security.oauth2.client.provider.keycloak.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.keycloak.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.keycloak.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=name
myapp.authentication.type=oauth
现在,对于现有的身份验证机制,我想添加对多个身份验证提供程序的支持:LDAP、Form-Login 等
关于这方面,我看过几篇文章:
- https://www.baeldung.com/spring-security-multiple-auth-providers
- Custom Authentication provider with Spring Security and Java Config
但是,对于我应该对现有代码库进行哪些更改以实现此目的,我没有任何具体的想法。
有人可以帮忙吗?谢谢。
我从您的代码开始创建了一个简化的设置,同时支持 OAuth2 和 Basic Auth。
/tenant2/**
将启动基本身份验证。
/**
(其他所有内容)触发 OAuth2 授权码身份验证。
实现这一点的关键是每个身份验证类型有一个 @Configuration
class。
让我们从控制器开始:
Tenant1HomeController
@Controller
public class Tenant1HomeController {
@GetMapping("/tenant1/home")
public String home() {
return "tenant1Home";
}
}
Tenant2HomeController
@Controller
public class Tenant2HomeController {
@GetMapping("/tenant2/home")
public String home() {
return "tenant2Home";
}
}
现在,配置 classes:
Tenant1SecurityConfiguration
@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority("ADMIN")
.antMatchers("/tenant1/**").authenticated()
.and()
.oauth2Login()
.and()
.cors()
.disable();
}
}
Tenant2SecurityConfiguration(注意 @Order(90)
,这很重要
@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher("/tenant2/**"))
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/tenant2/**").hasAuthority("BASIC_USER")
.and()
.httpBasic();
http.cors().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("BASIC_USER");
}
}
最后配置:
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: myclient
client-secret: c6dce03e-ea13-4b76-8aab-c876f5c2c1d9
provider:
keycloak:
issuer-uri: http://localhost:8180/auth/realms/myrealm
有了这个,如果我们点击 http://localhost:8080/tenant2/home
,将提示基本身份验证弹出窗口:
尝试 http://localhost:8080/tenant1/home
会将您带到 Keycloak 的登录表单:
更新:
用上面的配置配置一个多租户应用是完全可行的。
关键是每个身份验证提供商都与一组不同的用户(租户)合作,例如:
租户 1(OAuth2 身份验证):
@Configuration
public class Tenant1SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
...
.and()
.oauth2Login()
.and()
...
第一个用户子集由 OAuth2 提供商联合,在本例中为 Keycloak。
租户 2(基本/表单/xxx 身份验证):
@Order(90)
@Configuration
public class Tenant2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
...
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(XXX)
对于第二个租户,您可以使用指向不同用户存储库(LDAP、数据库...)的 userDetailsService。