Spring Oauth2: Redirect back to origin url after login successfully.
Spring OAuth2 登入成功後跳轉到原來的地址
本文介紹基於Spring Cloud Zuul
實現的OAuth2 Clinet
在跳轉到OAuth2 Server
的登入頁面成功登入後如何redirect
跳轉回原來的地址,即redirect back to origin url after login successfully
.
Spring OAuth SSO
的基本過程有以下幾步:
- 使用者訪問網站,開啟了一個連結(
origin url
) ; - 訪問請求傳送給
Zuul Gateway
伺服器(該伺服器同時也是OAuth2 Client
),伺服器判斷該請求是否訪問了受保護的資源; - 如果是訪問受保護的資源則判斷登入狀態,沒有登入則重定向到
OAuth2 Server
的登入頁面; - 使用者輸入賬戶資訊進行登入,登入成功後將使用者資訊保持在
Spring Security Context
中並進行頁面跳轉。
由於之前系統框架中只有一個客戶端,而且我們的客戶端是基於Vue
的單頁面應用,所以就在OAuth2 Server
中直接配置了一個登入成功後的預設地址。最近需要增加一個客戶端,在使用同一個OAuth2 Server
作為統一登入時就面臨著必須讓使用者從哪裡來回哪裡去的問題!經過查閱文件和跟蹤除錯,OAuth2 Client
在檢測到使用者未登入訪問受保護的資源時會直接redirect
到OAuth2 Server
,redirect的url是基於OAuth2 Client
配置的security.oauth2.client.user-authorization-uri
和security.oauth2.client.client-id
進行拼接而成的,如下所示:
http://127.0.0.1:8080/auth/oauth/authorize?client_id=oauth2_client_id&redirect_uri=http://127.0.0.1/login&response_type=code&state=B5c3xa
除了這段redirect url
中的資料之外,OAuth2 Client
沒有再給OAuth2 Server
任何其他資料,所以我需要將origin url
作為query string
引數放到這段redirect url
裡面去。解決方案分為三步:
- 在
OAuth2 Client
中獲取到origin url
並在組裝oauth2的redirect url
時新增到query string
中傳遞給OAuth2 Server
; OAuth2 Server
在自己的LoginSuccessHandler
中從request session
中拿出SPRING_SECURITY_SAVED_REQUEST
獲取到redirect url
並解析出original url
;response.sendRedirect(originUrl)
基於spring cloud zuul gateway
的OAuth2 Client
配置:
- 重寫
LoginUrlAuthenticationEntryPoint
將origin url
儲存在session
中; - 重寫
DefaultRedirectStrategy
將session
中的origin url
拼接在redirect url
中
/**
* UI代理伺服器,基於zuul的 oauth2 client.
*/
@SpringBootApplication
@EnableOAuth2Sso
@EnableZuulProxy
public class UIProxyApplication extends WebSecurityConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(UIProxyApplication.class, args);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println(logoutUrl);
http.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint("/login"));
http.authorizeRequests()
.antMatchers("/login", "/api/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
}
@Bean
public LogoutHandler logoutHandler() {
return new MyLogoutHandler();
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
filter.setRedirectStrategy(new OAuthRedirectStrategy());
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
class OAuthRedirectStrategy extends DefaultRedirectStrategy {
@Override
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Custom BMA SecurityConfiguration Redirecting to '" + redirectUrl + "'");
}
String requestUrl = request.getSession().getAttribute("requestUrl").toString();
redirectUrl += "&request_url=" + requestUrl;
response.sendRedirect(redirectUrl);
}
}
//轉發或者重定向到登入頁面
public class UnauthorizedEntryPoint extends LoginUrlAuthenticationEntryPoint {
public UnauthorizedEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
request.getSession().setAttribute("requestUrl", request.getRequestURL());
super.commence(request, response, authException);
}
}
}
OAuth2 Server
的LoginSuccessHandler
處理redirect url
並redirect
到origin url
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
Object principal = authentication.getPrincipal();
String username = "";
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else {
username = principal.toString();
}
if (request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST") != null) {
String savedRequest = request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST").toString();
String[] params = savedRequest.split("&");
for (int i = 0; i < params.length; i++) {
if (params[i].indexOf("request_url") != -1) {
String requestUrl = params[i].split("=")[1].split("]")[0];
response.sendRedirect(requestUrl);
}
}
} else {
super.onAuthenticationSuccess(request, response, token);
}
}
}
OAuth2 Server
中配置LoginSuccessHandler
/**
* 安全配置類
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//customize login page.
httpSecurity
.authorizeRequests().antMatchers(
"/login/**",
"/js/**",
"/css/**",
"/img/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").permitAll()
.and().csrf().disable()
httpSecurity.addFilter(myUsernamePasswordAuthenticationFilter());
}
@Bean
FilterRegistrationBean forwardedHeaderFilter() {
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(new ForwardedHeaderFilter());
filterRegBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegBean;
}
@Bean
public UsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception{
MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(loginSuccessHandler());
return filter;
}
@Bean
public LoginSuccessHandler loginSuccessHandler() {
return new LoginSuccessHandler();
}
}
一些說明:
OAuth2
是基於JWT
的,相關配置沒有在這裡給出;- 增加
UsernamePasswordAuthenticationFilter
和LoginSuccessHandler
是原有專案需求,不是為了這次的跳轉才加的; - 這個方法可能還不是
common practise
,有更好的方法歡迎交流! - 參考連結: https://stackoverflow.com/questions/51456479/stateless-spring-jwt-application-enableoauth2client
- https://segmentfault.com/a/1190000012137647
- https://blog.csdn.net/honghailiang888/article/details/52679264
- https://www.baeldung.com/spring-redirect-and-forward
相關文章
- URL origin 屬性
- .htaccess技巧: URL重寫(Rewrite)與重定向(Redirect)
- Nginx反向代理中使用proxy_redirect重定向urlNginx
- Spring Security Oauth2SpringOAuth
- Spring Cloud :: Security :: OAuth2 – ⓪ OAuth2模型詳述SpringCloudOAuth模型
- Spring MVC之redirect、forward和普通跳轉SpringMVCForward
- Spring security OAuth2 深入解析SpringOAuth
- Document Redirect 與 XHR Redirect區別
- 暢購商城(九):Spring Security Oauth2SpringOAuth
- Spring Security OAuth2 單點登入SpringOAuth
- [譯] Spring REST API + OAuth2 + AngularJSSpringRESTAPIOAuthAngularJS
- spring security oauth2 password授權模式SpringOAuth模式
- feed back 和 mail/call me backAI
- would dispatch back to the current handler URL [/doLogin] again. Check your ViewResolver setup!AIView
- SAP Fiori Launchpad url 引數 sap-app-origin-hint 的含義APP
- http之RedirectHTTP
- Spring Security原始碼分析十一:Spring Security OAuth2整合JWTSpring原始碼OAuthJWT
- Spring Security原始碼分析十:初識Spring Security OAuth2Spring原始碼OAuth
- Spring Cloud Security:Oauth2結合JWT使用SpringCloudOAuthJWT
- Spring Boot Oauth2快取UserDetails到EhcacheSpring BootOAuth快取AI
- 擁抱Spring全新OAuth2解決方案SpringOAuth
- spring security oauth2 搭建認證中心demoSpringOAuth
- 如何從一個 URL 字串中提取出origin (scheme+domain+port) 資訊?字串SchemeAI
- Spring Cloud Gateway + oauth2 跨域配置實現SpringCloudGatewayOAuth跨域
- Spring Cloud Security:Oauth2實現單點登入SpringCloudOAuth
- spring5.0 security oauth2 token 儲存失敗SpringOAuth
- Mosfet Bi-Directional Switch NMOS PMOS Back to Back
- Spring Security OAuth2之resource_id配置與驗證SpringOAuth
- spring boot 2.0 整合 oauth2 authorization code授權碼模式Spring BootOAuth模式
- spring boot 2.0 整合 security oauth2 password 模式和credentials 模式Spring BootOAuth模式
- spring mvc中獲取請求URLSpringMVC
- [譯] 學習 Spring Security(八):使用 Spring Security OAuth2 實現單點登入SpringOAuth
- spring-boot spring-security oauth2 /oauth/token報401,403 問題SpringbootOAuth
- 重定向Redirect 的知識
- 4. render, redirect, HttpResponse, reverseHTTP
- nginx的port_in_redirect配置Nginx
- forward和redirect的區別Forward
- Angular 伺服器端渲染兩個相關的 SERVER_REQUEST_URL 和 SERVER_REQUEST_ORIGINAngular伺服器Server