英文:
How to get oauth2 code param to redirect after login in the oauth2 auth code flow
问题
我的应用程序具有自定义的登录端点,spring-auth-server将用户代理重定向到/login
。
在以下调用之后重定向到login
curl --location 'http://localhost:8082/oauth2/authorize?response_type=code&client_id=oauth-dev&client_secret=secret&redirect_uri=https%3A%2F%2Fexample.com&state=state123'
当前的问题是,我的登录端点可以重定向到redirect_uri
:https://example.com,但我不知道如何获取oauth2的code参数,所以它变成了
"redirect:" + savedRequest.getParameterValues("redirect_uri") + "&code=" + code + "&state" = state;
登录端点
@PostMapping("/login")
public String login(final HttpServletRequest request, String username) {
// auth服务主要通过用户名/密码验证用户
authService.authenticate(username);
SavedRequest savedRequest = (SavedRequest) request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST");
return "redirect:" + savedRequest.getParameterValues("redirect_uri");
// 缺少code参数。如何找到oauth2的code参数?
}
SecurityConfig
@Autowired
private lateinit var passwordEncoder: PasswordEncoder
@Bean
@Order(1)
fun authorizationServerSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
http.getConfigurer(OAuth2AuthorizationServerConfigurer::class.java)
.oidc(Customizer.withDefaults())
http
.exceptionHandling { exceptions ->
exceptions.authenticationEntryPoint(LoginUrlAuthenticationEntryPoint ("/login"))
}
.oauth2ResourceServer {
it.jwt()
}
return http.build()
}
@Bean
fun authorizationServerSettings(): AuthorizationServerSettings {
return AuthorizationServerSettings.builder().build()
}
@Bean
fun authorizationService(): OAuth2AuthorizationService {
return InMemoryOAuth2AuthorizationService()
}
@Bean
fun registeredClientRepository(): RegisteredClientRepository {
val registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oauth-dev")
.clientSecret(passwordEncoder.encode("secret"))
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("https://example.com")
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(false)
.requireProofKey(false).build())
.build()
return InMemoryRegisteredClientRepository(registeredClient)
}
@Bean
fun jwkSource(): JWKSource<SecurityContext> {
val rsaKey = Jwks.generateRsa()
val jwkSet = JWKSet(rsaKey)
return JWKSource { jwkSelector: JWKSelector, securityContext: SecurityContext? -> jwkSelector.select(jwkSet) }
}
@Bean
fun jwtDecoder(jwkSource: JWKSource<SecurityContext>): JwtDecoder {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource)
}
具体来说,我在以下图表中的步骤9遇到了问题。如何让步骤9的登录服务找到oauth2的code参数?
我看到代码生成部分没有达到
因为有这个条件
if (!isPrincipalAuthenticated(principal)) {
并且principal是AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_ANONYMOUS]]
。
英文:
My app has custom login endpoint and the spring-auth-server redirects user agent to /login
Redirect to login
After the following call
curl --location 'http://localhost:8082/oauth2/authorize?response_type=code&client_id=oauth-dev&client_secret=secret&redirect_uri=https%3A%2F%2Fexample.com&state=state123'
The current issue is my login endpoint can redirect to the redirect_uri: https://example.com, but I don't find how to get the oauth2 code param so it becomes
"redirect:" + savedRequest.getParameterValues("redirect_uri") + "&code=" + code + "&state" = state;
login endpoint
@PostMapping("/login")
public String login(final HttpServletRequest request, String username) {
// the auth service mainly authenticates user by username/password
authService.authenticate(username);
SavedRequest savedRequest: = request.session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") as SavedRequest;
return "redirect:" + savedRequest.getParameterValues("redirect_uri");
// it's missing code param. how to find the oauth2 code param?
}
SecurityConfig
@Autowired
private lateinit var passwordEncoder: PasswordEncoder
@Bean
@Order(1)
fun authorizationServerSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
http.getConfigurer(OAuth2AuthorizationServerConfigurer::class.java)
.oidc(Customizer.withDefaults())
http
.exceptionHandling { exceptions ->
exceptions.authenticationEntryPoint(LoginUrlAuthenticationEntryPoint ("/login"))
}
.oauth2ResourceServer {
it.jwt()
}
return http.build()
}
@Bean
fun authorizationServerSettings(): AuthorizationServerSettings {
return AuthorizationServerSettings.builder().build()
}
@Bean
fun authorizationService(): OAuth2AuthorizationService {
return InMemoryOAuth2AuthorizationService()
}
@Bean
fun registeredClientRepository(): RegisteredClientRepository {
val registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oauth-dev")
.clientSecret(passwordEncoder.encode("secret"))
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("https://example.com")
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(false)
.requireProofKey(false).build())
.build()
return InMemoryRegisteredClientRepository(registeredClient)
}
@Bean
fun jwkSource(): JWKSource<SecurityContext> {
val rsaKey = Jwks.generateRsa()
val jwkSet = JWKSet(rsaKey)
return JWKSource { jwkSelector: JWKSelector, securityContext: SecurityContext? -> jwkSelector.select(jwkSet) }
}
@Bean
fun jwtDecoder(jwkSource: JWKSource<SecurityContext>): JwtDecoder {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource)
}
Specifically, I faced the issue at step 9 as shown in the following diagram. How can the login service at step 9 find the oauth2 code param?
I saw the code generation part is not reached
because of this condition
if (!isPrincipalAuthenticated(principal)) {
and the principal is AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_ANONYMOUS]]
答案1
得分: 1
你的问题询问了“在授权码流程中登录后如何获取重定向的状态和代码”,但没有提及用户实际是如何进行身份验证的,也没有说明是否成功调用了授权端点。你的登录端点似乎没有对用户进行身份验证,所以我不清楚它应该如何工作。
我可以看到 OAuth2AuthorizationCodeRequestAuthenticationToken 有一个名为 authorizationCode 的属性,但它是空的。
根据你描述的问题,听起来你实际上还没有调用 /oauth2/authorize
端点并生成一个 authorization_code
,所以下一步就是这个。
在验证用户后检索到的保存的请求实际上是对 /oauth2/authorize
端点的请求。你还没有 code
和 state
参数,因为这些是由授权端点创建/管理的。所以你只需从 SavedRequest
中重定向到 savedRequest.getRedirectUrl()
,然后就可以继续了。
你可以(并且可能应该)使用 HttpSessionRequestCache
而不是直接访问会话,像这样:
private RequestCache requestCache = new HttpSessionRequestCache();
// ...
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
String redirectUrl = savedRequest.getRedirectUrl();
**注意:**从你的问题中并不清楚 redirectUrl
有什么问题,但似乎你对此有了错误的期望。
如果这还没有完全解决问题,请提供更多关于你的登录端点正在执行什么以及在重定向到 SavedRequest
的 redirectUrl
时发生了什么的细节。
英文:
Your question asks "How to get state and code for redirect after login in auth code flow" but does not mention how the user is actually authenticated, or whether the Authorization Endpoint has been successfully invoked. Your login endpoint does not appear to authenticate the user, so I'm unclear on how it's supposed to work.
> I can see OAuth2AuthorizationCodeRequestAuthenticationToken has a attribute authorizationCode, but it's null
Based on your described issue, it sounds like you have not actually invoked the /oauth2/authorize
endpoint and generated an authorization_code
yet, and so that's the next step.
The saved request you are retrieving after authenticating the user is actually the request to the /oauth2/authorize
endpoint. You will not yet have the code
and state
parameters, as those are created/managed by the Authorization Endpoint. So you have only to redirect to the savedRequest.getRedirectUrl()
from the SavedRequest
and you should be good to go.
You can (and probably should) use HttpSessionRequestCache
instead of directly accessing the session, like this:
private RequestCache requestCache = new HttpSessionRequestCache();
// ...
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
String redirectUrl = savedRequest.getRedirectUrl();
Note: It's not clear from your question what about the redirectUrl
is not working, but it seems that you're expecting the wrong thing here.
If that doesn't quite clear it up, please add more details of what you're login endpoint is doing and what isn't working when you redirect to the redirectUrl
from the SavedRequest
.
答案2
得分: 0
我找到了解决方案。以下是我学到的内容:
在控制器内部手动调用安全过滤链不建议,因为它可能会绕过正常的Spring Security流程,潜在地导致安全漏洞。
一般情况下,不建议在控制器内部手动调用AuthenticationManager。AuthenticationManager通常由安全过滤链在认证过程中调用,手动调用可能会导致意外行为或安全漏洞。
因此,登录的自定义端点被重新编写,使用.formLogin()
,以及自定义的successHandler
和自定义的failureHandler
。
@Component
class LoginSuccessHandler() : SavedRequestAwareAuthenticationSuccessHandler() {
private var requestCache: RequestCache = HttpSessionRequestCache()
override fun onAuthenticationSuccess(
request: HttpServletRequest,
response: HttpServletResponse,
authentication: Authentication
) {
val savedRequest = this.requestCache.getRequest(request, response)
// 登录不是来自授权码流程
if (savedRequest == null) {
// 自定义逻辑
return
}
// 授权码流程
val targetUrl = savedRequest.redirectUrl
redirectStrategy.sendRedirect(request, response, targetUrl)
}
override fun setRequestCache(requestCache: RequestCache) {
this.requestCache = requestCache
}
}
@Component
class LoginFailureHandler(
) : SimpleUrlAuthenticationFailureHandler() {
override fun onAuthenticationFailure(
request: HttpServletRequest,
response: HttpServletResponse,
ex: AuthenticationException
) {
// 自定义逻辑
super.onAuthenticationFailure(request, response, ex)
}
}
英文:
I found the solution. Below is what I learnd:
Manually invoking the security filter chain within a controller is not recommended because it can bypass the normal Spring Security flow, potentially leading to security vulnerabilities.
In general, it is also not recommended to invoke AuthenticationManager manually within a controller. The AuthenticationManager is typically invoked by the security filter chain during the authentication process, and manually invoking it may result in unexpected behavior or security vulnerabilities
Thus, the custom endpoint for login is re-written using .formLogin()
and a custom successHandler
and a custom failureHandler
@Component
class LoginSuccessHandler() : SavedRequestAwareAuthenticationSuccessHandler() {
private var requestCache: RequestCache = HttpSessionRequestCache()
override fun onAuthenticationSuccess(
request: HttpServletRequest,
response: HttpServletResponse,
authentication: Authentication
) {
val savedRequest = this.requestCache.getRequest(request, response)
// login is not from auth code flow
if (savedRequest == null) {
// custom logic
return
}
// auth code flow
val targetUrl = savedRequest.redirectUrl
redirectStrategy.sendRedirect(request, response, targetUrl)
}
override fun setRequestCache(requestCache: RequestCache) {
this.requestCache = requestCache
}
}
@Component
class LoginFailureHandler(
) : SimpleUrlAuthenticationFailureHandler() {
override fun onAuthenticationFailure(
request: HttpServletRequest,
response: HttpServletResponse,
ex: AuthenticationException
) {
// custom logic
super.onAuthenticationFailure(request, response, ex)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论