我正在实现 Spring 授权服务器的非常基本的示例。我已经参考了 spring 授权服务器入门文档。我已经在 SpringSuiteTool4 中为该项目创建了 Spring 入门项目。
当我运行项目并在浏览器中调用 /oauth2/token url 时,它会将其重定向到登录页面。当我输入正确的用户名和密码时,它会将我重定向到/错误页面。
这是我正在使用的代码。
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
/**
*
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain webFilterChainForOAuth(HttpSecurity httpSecurity) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(httpSecurity);
httpSecurity.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults());
httpSecurity.exceptionHandling(e -> e.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")));
return httpSecurity.build();
}
@Order(2)
@Bean
public SecurityFilterChain appSecurity(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests(request -> request.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return httpSecurity.build();
}
@Bean
public UserDetailsService userDetailsService() {
var userDetails = User.withDefaultPasswordEncoder()
.username("prem")
.password("password")
.authorities("read")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
var registeredClients = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("public-client-app")
.clientSecret("secret")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.redirectUri("http://127.0.0.1:8083/login/oauth2/code/public-client-app")
.authorizationGrantTypes(
grantType -> {
grantType.add(AuthorizationGrantType.AUTHORIZATION_CODE);
grantType.add(AuthorizationGrantType.REFRESH_TOKEN);
grantType.add(AuthorizationGrantType.CLIENT_CREDENTIALS);
}
).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()/*requireProofKey(true)*/).build();
return new InMemoryRegisteredClientRepository(registeredClients);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.tokenEndpoint("/token")
.build();
}
@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
var keys = generateRSAKey();
var publicKey = (RSAPublicKey) keys.getPublic();
var privateKey = (RSAPrivateKey) keys.getPrivate();
var 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);
}
}
这是以下项目gradle。
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.opal.web.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
请帮助我理解这个问题。
这是调试日志条目,它们没有给我任何线索。
[2m2024-02-20T19:04:03.429+05:30[0;39m [32m INFO[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.a.c.c.C.[Tomcat].[localhost].[/] [0;39m [2m:[0;39m Initializing Spring DispatcherServlet 'dispatcherServlet'
[2m2024-02-20T19:04:03.429+05:30[0;39m [32m INFO[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Initializing Servlet 'dispatcherServlet'
[2m2024-02-20T19:04:03.430+05:30[0;39m [32m INFO[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Completed initialization in 1 ms
[2m2024-02-20T19:04:03.440+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /oauth2/token
[2m2024-02-20T19:04:03.444+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
[2m2024-02-20T19:04:03.452+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.s.w.s.HttpSessionRequestCache [0;39m [2m:[0;39m Saved request http://localhost:8080/oauth2/token?continue to session
[2m2024-02-20T19:04:03.453+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [2m[0;39m[36mo.s.s.web.DefaultRedirectStrategy [0;39m [2m:[0;39m Redirecting to http://localhost:8080/login
[2m2024-02-20T19:04:03.456+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /login
[2m2024-02-20T19:04:11.642+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing POST /login
[2m2024-02-20T19:04:11.851+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mo.s.s.a.dao.DaoAuthenticationProvider [0;39m [2m:[0;39m Authenticated user
[2m2024-02-20T19:04:11.853+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36m.s.ChangeSessionIdAuthenticationStrategy[0;39m [2m:[0;39m Changed session id from 3E745EDCF7CE1C679F4B66F87F44289B
[2m2024-02-20T19:04:11.853+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mo.s.s.w.csrf.CsrfAuthenticationStrategy [0;39m [2m:[0;39m Replaced CSRF Token
[2m2024-02-20T19:04:11.853+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mw.c.HttpSessionSecurityContextRepository[0;39m [2m:[0;39m Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=prem, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[read]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=3E745EDCF7CE1C679F4B66F87F44289B], Granted Authorities=[read]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade@2009ab45]
[2m2024-02-20T19:04:11.854+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mw.a.UsernamePasswordAuthenticationFilter[0;39m [2m:[0;39m Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=prem, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[read]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=3E745EDCF7CE1C679F4B66F87F44289B], Granted Authorities=[read]]
[2m2024-02-20T19:04:11.854+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-3][0;39m [2m[0;39m[36mo.s.s.web.DefaultRedirectStrategy [0;39m [2m:[0;39m Redirecting to http://localhost:8080/oauth2/token?continue
[2m2024-02-20T19:04:11.858+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /oauth2/token?continue
[2m2024-02-20T19:04:11.859+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mo.s.s.w.s.HttpSessionRequestCache [0;39m [2m:[0;39m Loaded matching saved request http://localhost:8080/oauth2/token?continue
[2m2024-02-20T19:04:11.860+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mw.c.HttpSessionSecurityContextRepository[0;39m [2m:[0;39m Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=prem, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[read]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=3E745EDCF7CE1C679F4B66F87F44289B], Granted Authorities=[read]]]
[2m2024-02-20T19:04:11.860+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /oauth2/token?continue
[2m2024-02-20T19:04:11.868+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /error?continue
[2m2024-02-20T19:04:11.868+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mw.c.HttpSessionSecurityContextRepository[0;39m [2m:[0;39m Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=prem, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[read]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=3E745EDCF7CE1C679F4B66F87F44289B], Granted Authorities=[read]]]
[2m2024-02-20T19:04:11.868+05:30[0;39m [32mDEBUG[0;39m [35m53207[0;39m [2m---[0;39m [2m[nio-8080-exec-4][0;39m [2m[0;39m[36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /error?continue
我尝试了不同的组合并检查了端口更改。但这些解决方案并没有奏效。 我本来以为它能正常工作。
您必须设置 OAuth2 客户端并使用它来启动授权代码流程。
使用 Spring 客户端,将浏览器指向
{client-uri}/oauth2/authorization/{registration-id}
使用 Postman,在“授权”选项卡中填写 OAuth2 表单。
授权代码流的第一步是当用户重定向时,客户端重定向到授权端点,而不是稍后调用的令牌端点(根据您的conf,带有客户端ID和客户端秘密)使用授权代码从授权服务器返回客户端。
顺便说一句,如果您对 OAuth2 及其 Spring 实现了解甚少,Spring 授权服务器可能不是最佳选择...