在获得客户端OIDC的一些经验之后和Keycloak作为供应商我想仔细看看Spring的授权服务器。
不幸的是,我不能得到最简单的例子运行预期。所以我在文档中设置了一个服务器。我也试过链接的例子
通过没有URI参数的表单登录的标准登录可以正常工作,当我像下面的例子那样附加标准参数时,它就会失效。
http://localhost:8081/oauth2/authorize?client_id=oauth2-proxy&response_type=code&scope=openid
它会立即抛出一个错误在后台,像这样:
2023-01-13T09:10:34.837+01:00 DEBUG 4126 --- [nio-9000-exec-1] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:9000/login;jsessionid=542C9BFAE8B26DD2DB33597F8040993D
2023-01-13T09:10:34.840+01:00 DEBUG 4126 --- [nio-9000-exec-2] s.s.w.f.HttpStatusRequestRejectedHandler : Rejecting request due to: The request was rejected because the URL contained a potentially malicious String ";"
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
它仍然会继续,但最终会以500退出。感谢任何帮助,增加日志级别在这里没有帮助
2023-01-13T09:10:41.666+01:00 DEBUG 4126 --- [nio-9000-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
2023-01-13T09:10:41.679+01:00 DEBUG 4126 --- [nio-9000-exec-5] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, text/html;q=0.8]
2023-01-13T09:10:41.681+01:00 DEBUG 4126 --- [nio-9000-exec-5] o.s.web.servlet.DispatcherServlet : Completed 500 INTERNAL_SERVER_ERROR
我希望登录工作,而不是保释与500:
@Configuration
public class SecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user1")
.password("user1")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oauth2-proxy")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://localhost:8081/")
//.redirectUri("http://localhost:8081/login/oauth2/code/oauth2-proxy-oidc")
//.redirectUri("http://localhost:8081/authorized")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope(OidcScopes.EMAIL)
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
application.yml
:
#application
server:
port: 8081
tomcat.threads.max: 20
#monitoring
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "health,prometheus,heapdump"
logging:
level:
root: debug
org.springframework: debug
总结一下,这是我学到的东西:
- 你的url必须包含一个重定向uri,因为它是一个必填字段
- 重定向uri是特定于您的客户端,所以查看正确的url 重定向uri当然要在服务器上注册,在RegisterdClientRepository中
- 但是具体来说,只有当服务器知道url提供的 时,这个才会用于模式匹配。
- 重定向uri必须不包含localhost,至少在标准配置中是禁用的 你应该启用loglevel "trace"为spring安全真正看到所有的错误
如果你遵守这些规则,你就会成功。否则,你会在前端遇到一个通用的错误999,没有进一步的线索…
如果你想了解更多细节,我已经编译了一个简短的视频,并与Github Repo连接https://youtu.be/ZrG7fdTDjzw