java - 使用额外回调处理自定义登录
问题描述
我正在使用 Spring Security 5 和 Spring Boot 2.1 构建 OAuth2 提供程序服务器。
就我而言,我的服务器必须与某个外部服务器通信以验证用户身份。这个外部服务器生活在恐龙时代,因此没有使用像 OAuth 这样的通用身份验证机制。所以我必须劫持登录请求,重定向到恐龙服务器,手动处理该身份验证(不幸的是,其中包括回调),然后返回 Spring Security 以批准登录请求并确保用户获得访问令牌。
劫持登录请求如下:
@Override
protected void configure ( HttpSecurity http ) throws Exception {
http
.requestMatchers()
.antMatchers( "/login", "/oauth/authorize", "/manuallogin" )
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage( "/manuallogin" )
.permitAll()
.and().csrf().disable();
}
@RestController
public class MainLoginController {
@RequestMapping("/manuallogin")
ResponseEntity<Object> interceptLoginRequest ( ){
return ResponseEntity.status( HttpStatus.FOUND )
.location( URI.create( dinosaurServer.getLoginUrl() ) )
.build();
}
@RequestMapping("/handshakeWithDinosaur")
@Override
public ResponseEntity<Object> handshakeWithDinosaur ( String dinosaursToken ) {
Principal principal = getUserPrincipalFromDinosaur();
// somehow continue login, here is the problem
}
如您所见,我需要接受另一个回调,因此我丢失了原始登录请求,我无法对此发送响应。
我想出了以下解决方案,通过调用 OAuth2 客户端的回调 URL 来缩短。
Principal principal = getUserPrincipalFromDinosaur();
Credential credential = new Credential();
credential.setUsername( "john" );
credential.setPassword( "123" );
PreAuthenticatedAuthenticationToken preAuthenticatedAuthenticationToken = new PreAuthenticatedAuthenticationToken( principal, credential);
preAuthenticatedAuthenticationToken.setAuthenticated( true );
SecurityContextHolder.getContext().setAuthentication( preAuthenticatedAuthenticationToken);
return ResponseEntity.status( HttpStatus.FOUND )
.location( URI.create( String.format( oauth2client.getCallbackUrl(), dinosaursToken ) ))
.build();
但是,这不起作用,因为 spring 不接受身份验证。
我必须以某种方式继续原始登录请求并对用户进行身份验证。即使您阅读了所有这些,也非常感谢您:)
解决方案
对于遇到这个问题的任何绝望的灵魂,这里是解决方案:
@RestController
public class MainLoginController {
@RequestMapping("/manuallogin")
ResponseEntity<Object> interceptLoginRequest ( ){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
DefaultSavedRequest springSecuritySavedRequest = (DefaultSavedRequest) requestAttributes.getRequest()
.getSession()
.getAttribute( "SPRING_SECURITY_SAVED_REQUEST" );
queryString = springSecuritySavedRequest.getQueryString();
request.getSession().setAttribute( "queryString", queryString );
return ResponseEntity.status( HttpStatus.FOUND )
.location( URI.create( dinosaurServer.getLoginUrl() ) )
.build();
}
@RequestMapping("/handshakeWithDinosaur")
public ResponseEntity<Object> handshakeWithDinosaur ( String dinosaursToken ) {
Authentication authentication = this.authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
dino.getUser(), dino.getPass()
)
);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication( authentication );
request.getSession().setAttribute( SPRING_SECURITY_CONTEXT_KEY, sc );
String queryString = String.valueOf( request.getSession().getAttribute( "queryString" ) );
return ResponseEntity.status( HttpStatus.FOUND )
.location( URI.create( String.format( "%s?%s",SPRING_AUTH_ENDPOINT, queryString ) ) )
.build();
}
@Component
public class AuthProviderForDinosaur implements AuthenticationProvider {
@Override
public Authentication authenticate ( Authentication authentication ) throws AuthenticationException {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add( new SimpleGrantedAuthority( "ROLE_USER" ) );
return new UsernamePasswordAuthenticationToken( authentication.getName(), authentication.getCredentials(), grantedAuths );
}
@Override
public boolean supports ( Class<? extends Object> authentication ) {
return ( UsernamePasswordAuthenticationToken.class.isAssignableFrom( authentication ) );
}
}
基本上,我启用了会话并让 Spring 在会话中为我保留请求,同时服务器与恐龙服务器对话并完成握手。完成后,向 Spring 询问先前请求的参数以通过 Spring Security 继续授权。
推荐阅读
- scala - scala,过滤RDD
- python - Apriori算法的输出是否应该是一对一的关系
- git - 从存储库获取分支依赖
- system - 如何使用 Windows 防火墙阻止进程?
- spring-boot - Jprofiler 不会在应用程序崩溃时分离
- c++ - 我可以使用 cmake/make 编译所有且仅编译我使用的 cpp/hpp 文件吗?
- r - 如何解决这个与原子向量有关的 R 错误消息?
- javascript - 无法在 Angular 7 中使用 D3 条形图读取未定义的属性“地图”
- google-app-maker - 如何通过代码样式化不同类型输入控件的值?
- deep-learning - 在 OpenNMT-py 中的翻译过程中获得对齐/注意