Kai*_*kun 4 horizontal-scrolling spring-boot kubernetes oauth2-server
我有一个使用 JDBC 实现的 spring boot oauth2 服务器。它通过@EnableAuthorizationServer 配置为授权服务器。
我想水平扩展该应用程序,但它似乎无法正常工作。
仅当我有一个服务器实例(Pod)时,我才能连接。
我使用来自另一个客户端服务的 autorisation_code_client 授权来获取令牌。因此,首先客户端服务将用户重定向到 oauth2 服务器表单,然后一旦用户通过身份验证,他应该被重定向到客户端服务,并在 url 上附加代码,最后客户端使用该代码来请求 oauth2 服务器再次并获取token。
如果我有多个 oauth2-server 实例,则用户根本不会被重定向。就一个实例来说,效果很好。
当我实时检查两个实例的日志时,我可以看到身份验证在其中一个实例上进行。我没有任何具体错误,用户只是没有被重定向。
有没有办法将 oauth2-server 配置为无状态或其他方式来解决该问题?
这是我的配置,AuthorizationServerConfigurerAdapter 实现。
@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource oauthDataSource() {
return DataSourceBuilder.create().build();
}
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Bean
public JdbcClientDetailsService clientDetailsSrv() {
return new JdbcClientDetailsService(oauthDataSource());
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(oauthDataSource());
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(oauthDataSource());
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(oauthDataSource());
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
@Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsSrv());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)
.approvalStore(approvalStore())
//.approvalStoreDisabled()
.authorizationCodeServices(authorizationCodeServices())
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer());
}
}
Run Code Online (Sandbox Code Playgroud)
主要班级
@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
@EnableConfigurationProperties
@EnableFeignClients("com.oauth2.proxies")
public class AuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}
Run Code Online (Sandbox Code Playgroud)
网络安全配置
@Configuration
@Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return new JdbcUserDetails();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception { // @formatter:off
http.requestMatchers()
.antMatchers("/",
"/login",
"/login.do",
"/registration",
"/registration/confirm/**",
"/registration/resendToken",
"/password/forgot",
"/password/change",
"/password/change/**",
"/oauth/authorize**")
.and()
.authorizeRequests()//autorise les requetes
.antMatchers(
"/",
"/login",
"/login.do",
"/registration",
"/registration/confirm/**",
"/registration/resendToken",
"/password/forgot",
"/password/change",
"/password/change/**")
.permitAll()
.and()
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login.do")
.usernameParameter("username")
.passwordParameter("password")
.and()
.userDetailsService(userDetailsServiceBean());
} // @formatter:on
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceBean()).passwordEncoder(passwordEncoder());
}
}
Run Code Online (Sandbox Code Playgroud)
客户端 WebSecurityConfigurerAdapter
@EnableOAuth2Sso
@Configuration
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers(
"/",
"/index.html",
"/login**",
"/logout**",
//resources
"/assets/**",
"/static/**",
"/*.ico",
"/*.js",
"/*.json").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
}
}
Run Code Online (Sandbox Code Playgroud)
oauth2 配置属性
oauth2-server 是 kubernetes 上的服务名称(负载均衡器),也是服务器路径,这就是它出现两次的原因。
security:
oauth2:
client:
clientId: **********
clientSecret: *******
accessTokenUri: https://oauth2-server/oauth2-server/oauth/token
userAuthorizationUri: https://oauth2.mydomain.com/oauth2-server/oauth/authorize
resource:
userInfoUri: https://oauth2-server/oauth2-server/me
Run Code Online (Sandbox Code Playgroud)
这里有一个重要的细节,userAuthorizationUri的值是从k8s集群外部访问oauth2-server的地址。如果用户未连接并尝试访问客户端服务的 /login 路径,则客户端服务会将该地址发送回带有 302 http 代码的响应。然后用户被重定向到 oauth2-server 的 /login 路径。
https://oauth2.mydomain.com 的目标是 Nginx Ingress 控制器,用于处理到负载均衡器服务的重定向。
这是这个问题的解决方案。这根本不是 Spring 的问题,而是 Nginx Ingress 控制器的错误配置。
身份验证过程分几个阶段完成:
1 - 用户单击以客户端服务器的 /login 路径为目标的登录按钮
2 - 客户端服务器,如果用户尚未经过身份验证,则向浏览器发送带有 302 http 代码的响应,以将用户重定向到 oauth2-服务器,重定向的值由安全值组成 。 oauth2.client.userAuthorizationUri属性和浏览器将使用的重定向 url,以允许客户端服务器在用户通过身份验证后获取令牌。该网址如下所示:
h*tps://oauth2.mydomain.com/oauth2-server/oauth/authorize?client_id=autorisation_code_client&redirect_uri=h*tps://www.mydomain.com/login&response_type=code&state=bSWtGx
Run Code Online (Sandbox Code Playgroud)
3 - 用户被重定向到上一个网址
4 - oauth2-server 将 302 http 代码发送到浏览器,其中包含 oauth2-server 的登录 url,h*tps://oauth2.mydomain.com/oauth2-server/login
5 - 用户提交他的凭据,如果正确,则创建令牌。
6 - 用户被重定向到与第二步相同的地址,oauth 服务器将信息添加到redirect_uri 值
7 - 用户被重定向到客户端服务器。响应的重定向部分如下所示:
location: h*tps://www.mydomain.com/login?code=gnpZ0r&state=bSWtGx
Run Code Online (Sandbox Code Playgroud)
8 - 客户端服务器联系 oauth2 服务器并从代码中获取令牌以及对其进行身份验证的状态。oauth2 服务器的实例与用户用于验证自己身份的实例不同并不重要。这里客户端-服务器使用 security.oauth2.client.accessTokenUri 的值来获取令牌,这是针对 oauth2 服务器 Pod 的内部负载平衡服务地址,因此它不会通过任何 Ingress 控制器。
因此,在步骤 3 到 6 中,用户必须通过负载均衡器服务前面的 Ingress 控制器与 oauth2-server 的同一实例进行通信。
这可以通过使用一些注释配置 Nginx Ingress 控制器来实现:
"annotations": {
...
"nginx.ingress.kubernetes.io/affinity": "cookie",
"nginx.ingress.kubernetes.io/session-cookie-expires": "172800",
"nginx.ingress.kubernetes.io/session-cookie-max-age": "172800",
"nginx.ingress.kubernetes.io/session-cookie-name": "route"
}
Run Code Online (Sandbox Code Playgroud)
这样我们就可以确保用户在身份验证过程中被重定向到 oauth2-server 的相同 pod/实例,只要他使用相同的 cookie 进行标识。
关联会话机制是扩展身份验证服务器和客户端服务器的好方法。一旦用户通过身份验证,他将始终使用相同的客户端实例并保留其会话信息。
感谢克里斯蒂安·阿尔塔米拉诺·阿亚拉的帮助。
| 归档时间: |
|
| 查看次数: |
1767 次 |
| 最近记录: |