基于Spring Security的OAuth2.0授权服务器自定义登录实现问询
Spring Security OAuth2授权服务器自定义登录流程集成问题
当前使用的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-authorization-server</artifactId> <version>0.3.0</version> </dependency>
现有正常运行的授权码(带PKCE)流程
- 客户端调用授权服务器的授权端点,示例请求:
https://authorization-server.com/authorize?response_type=code&client_id=zMP_79LS2UPtyFa1z0TrdwoN&redirect_uri=https://www.oauth.com/playground/authorization-code-with-pkce.html&scope=photo+offline_access&state=0OaV1kmkkK8yFbqe&code_challenge=WVe6cGEQGlHk3hXXpyUo4hvs2uzCg4j3-oXfnX9L6HA&code_challenge_method=S256
- 授权服务器渲染登录表单完成用户认证
- 授权服务器向客户端的redirect_uri返回授权码
- 客户端调用令牌端点,携带授权码,示例请求:
https://authorization-server.com/token?grant_type=authorization_code&client_id=zMP_79LS2UPtyFa1z0TrdwoN&client_secret=VvfjKzAWIr0bxWKTUvzC48NkzlhwnAuSOsWOb6C8AdG7GH5O&redirect_uri=https://www.oauth.com/playground/authorization-code-with-pkce.html&code=z9dJErNexuAL0VRaOu9AfxhMuOJL-IT7zY5HILeKFW5XmFOT&code_verifier=Q3TsNTc5__76nzRtp43zjVJ3yxD_P1WD-khqJ7rdTxS_l73w
- 授权服务器返回Access Token
需求修改
希望将登录表单改为由React前端渲染,流程调整为:
- 客户端渲染登录表单
- 客户端通过RESTful接口
/auth/login将用户名/密码发送至后端
需要实现:
- 通过自定义Authentication Provider完成用户认证
- 将已认证用户注入OAuth2框架,让授权服务器无需渲染登录表单即可直接向redirect_uri发送授权码
已尝试的代码实现
1. 自定义认证相关实现
WebSecurity配置类
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class WebSecurityConfig { private final CustomAuthenticationProvider customAuthenticationProvider; @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class); builder.userDetailsService(customUserDetailService); builder.authenticationProvider(customAuthenticationProvider); return builder.build(); } }
自定义AuthenticationProvider
@Service @RequiredArgsConstructor public class CustomAuthenticationProvider implements AuthenticationProvider { private final CustomUserDetailService customUserDetailService; private final PasswordEncoder passwordEncoder; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = authentication.getCredentials().toString(); E2AuthUserDetails user = (E2AuthUserDetails) customUserDetailService.loadUserByUsername(username); return checkPassword(user, password); } private Authentication checkPassword(UserDetails user, String rawLoginPassword) { String dbPassword = user.getPassword(); if (passwordEncoder.matches(rawLoginPassword, dbPassword)) { return new UsernamePasswordAuthenticationToken(user.getUsername(), dbPassword, user.getAuthorities()); } else { throw new BadCredentialsException("Bad Credentials"); } } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } }
认证服务类
@Service @RequiredArgsConstructor public class AuthService { private final AuthenticationManager authenticationManager; public LoginResponse attemptLogin(String email, String password) { var authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(email, password) ); SecurityContextHolder.getContext().setAuthentication(authentication); } }
2. 授权服务器配置
@Configuration(proxyBeanMethods = false) public class AuthorizationServerConfig { @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private PasswordEncoder passwordEncoder; // 添加自定义安全过滤器并设置为链中第一个 @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); return http.formLogin(Customizer.withDefaults()).build(); } // 自定义客户端注册 @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("client") .clientSecret(passwordEncoder.encode("secret")) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUri("http://spring.io/auth") .scope(OidcScopes.OPENID) .build(); return new InMemoryRegisteredClientRepository(registeredClient); } // 配置OAuth端点及其他默认设置 @Bean public ProviderSettings providerSettings() { return ProviderSettings.builder() //.issuer("http://auth-server:9000") .build(); } }
内容的提问来源于stack exchange,提问作者stackoverflowenjoyer
相关产品推荐
相关产品推荐

