Spring 引导 - 使用 JWT、OAuth 以及单独的资源和 Auth 服务器
Spring Boot - Using JWT, OAuth, and Separate Resource and Auth Servers
我正在尝试构建一个使用 JWT 令牌和 OAuth2 协议的 Spring 应用程序。由于 this tutorial. However, I am struggling with getting the Resource Server to function properly. From following the article, and thanks to a response to a ,我有身份验证服务器 运行ning,这是我当前的尝试:
资源服务器的安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.encoding-strength}")
private Integer clientID;
@Value("${security.security-realm}")
private String securityRealm;
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean ResourceServerTokenServices tokenService() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Override
public AuthenticationManager authenticationManager() throws Exception {
OAuth2AuthenticationManager authManager = new OAuth2AuthenticationManager();
authManager.setTokenServices(tokenService());
return authManager;
}
}
资源服务器配置:
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private ResourceServerTokenServices tokenServices;
@Value("${security.jwt.resource-ids}")
private String resourceIds;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceIds).tokenServices(tokenServices);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().and().authorizeRequests().antMatchers("/actuator/**", "/api-docs/**").permitAll()
.antMatchers("/**").authenticated();
}
}
授权服务器的安全配置(来自著名教程):
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.encoding-strength}")
private Integer encodingStrength;
@Value("${security.security-realm}")
private String securityRealm;
@Autowired
private UserDetailsService userDetailsService;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new ShaPasswordEncoder(encodingStrength));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName(securityRealm)
.and()
.csrf()
.disable();
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
@Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
现在,当我尝试向资源服务器发出请求时,收到如下错误:
{"error":"invalid_token","error_description":"Invalid access token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXh
wIjoxNTE1MTE3NTU4LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF"}
我有几个问题:
- 据我了解,该问题通常与令牌存储有关。使用 JwtTokenStore 时如何处理服务器分离?
- 其次,目前,我的 Resource 应用依赖于对密钥的访问。根据我对 JWT 和 0Auth 规范的理解,这应该不是必需的。相反,我应该能够将验证委托给身份验证服务器本身。从 Spring 文档中,我认为以下 属性 可能适用
security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token
。但是,如果我尝试不依赖我的资源服务器的密钥,那么我 运行 会在设置我的 ResourceServerTokenService 时遇到困难。该服务需要一个令牌存储,JWTTokenStore 使用 JwtAccessTokenConverter,转换器使用一个密钥(删除密钥导致我之前遇到的相同 invalid token
错误)。
我真的很难找到说明如何分离 Auth 和 Resource 服务器的文章。如有任何建议,我们将不胜感激。
编辑:
实际上,资源服务器的代码现在无法编译并显示以下消息:
Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key
我尝试了 spring oauth,但遇到了同样的错误:
Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key
我的错误是我的 public 证书是:
-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
tadadada
-----END CERTIFICATE-----
这是不允许的。删除证书,只需让此文件中的 public 密钥:
-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
并且启动错误将消失。
关于你的第二个问题,我是这样理解的:
认证服务器给你一个加密令牌(用私钥加密),包含你用户的所有权限。
资源服务器用public密钥解密令牌,并假定令牌中包含的权限为TRUE。
希望对您有所帮助。
我 运行 遇到这个问题,当时我的 public 密钥格式如下:
"-----BEGIN RSA PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END RSA PUBLIC KEY-----\n"
当我将其更改为:
"-----BEGIN PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END PUBLIC KEY-----\n"
密钥已被接受。
我正在尝试构建一个使用 JWT 令牌和 OAuth2 协议的 Spring 应用程序。由于 this tutorial. However, I am struggling with getting the Resource Server to function properly. From following the article, and thanks to a response to a
资源服务器的安全配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.encoding-strength}")
private Integer clientID;
@Value("${security.security-realm}")
private String securityRealm;
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean ResourceServerTokenServices tokenService() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Override
public AuthenticationManager authenticationManager() throws Exception {
OAuth2AuthenticationManager authManager = new OAuth2AuthenticationManager();
authManager.setTokenServices(tokenService());
return authManager;
}
}
资源服务器配置:
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private ResourceServerTokenServices tokenServices;
@Value("${security.jwt.resource-ids}")
private String resourceIds;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceIds).tokenServices(tokenServices);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().and().authorizeRequests().antMatchers("/actuator/**", "/api-docs/**").permitAll()
.antMatchers("/**").authenticated();
}
}
授权服务器的安全配置(来自著名教程):
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.encoding-strength}")
private Integer encodingStrength;
@Value("${security.security-realm}")
private String securityRealm;
@Autowired
private UserDetailsService userDetailsService;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new ShaPasswordEncoder(encodingStrength));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName(securityRealm)
.and()
.csrf()
.disable();
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
@Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
现在,当我尝试向资源服务器发出请求时,收到如下错误:
{"error":"invalid_token","error_description":"Invalid access token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXh
wIjoxNTE1MTE3NTU4LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF"}
我有几个问题:
- 据我了解,该问题通常与令牌存储有关。使用 JwtTokenStore 时如何处理服务器分离?
- 其次,目前,我的 Resource 应用依赖于对密钥的访问。根据我对 JWT 和 0Auth 规范的理解,这应该不是必需的。相反,我应该能够将验证委托给身份验证服务器本身。从 Spring 文档中,我认为以下 属性 可能适用
security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token
。但是,如果我尝试不依赖我的资源服务器的密钥,那么我 运行 会在设置我的 ResourceServerTokenService 时遇到困难。该服务需要一个令牌存储,JWTTokenStore 使用 JwtAccessTokenConverter,转换器使用一个密钥(删除密钥导致我之前遇到的相同invalid token
错误)。
我真的很难找到说明如何分离 Auth 和 Resource 服务器的文章。如有任何建议,我们将不胜感激。
编辑: 实际上,资源服务器的代码现在无法编译并显示以下消息:
Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key
我尝试了 spring oauth,但遇到了同样的错误:
Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key
我的错误是我的 public 证书是:
-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
tadadada
-----END CERTIFICATE-----
这是不允许的。删除证书,只需让此文件中的 public 密钥:
-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
并且启动错误将消失。
关于你的第二个问题,我是这样理解的:
认证服务器给你一个加密令牌(用私钥加密),包含你用户的所有权限。
资源服务器用public密钥解密令牌,并假定令牌中包含的权限为TRUE。
希望对您有所帮助。
我 运行 遇到这个问题,当时我的 public 密钥格式如下:
"-----BEGIN RSA PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END RSA PUBLIC KEY-----\n"
当我将其更改为:
"-----BEGIN PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END PUBLIC KEY-----\n"
密钥已被接受。