Spring Oauth2: Redirect back to origin url after login successfully.
阿新 • • 發佈:2018-12-27
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();
}
}
一些說明: