學習本章之前,可以先了解下上篇Spring Security基本配置。
本篇想要達到這樣幾個目的:
1、訪問調用者服務時,如果是html請求,則跳轉到登錄頁,否則返回401狀態碼和錯誤信息
2、調用方可自定義登錄頁面。如果沒有配置,則使用認證中心的標准登錄頁
對於第一點,可以畫個圖:
接下來看具體的配置:
spring-security-browser
先建一個認證中需要用到的常量類:
/** * Security相關常量類 */ public class SecurityConst { /** 默認登錄頁url */ public static final String AUTH_REQUIRE = "/authentication/require"; /** 登錄頁請求url */ public static final String AUTH_FORM = "/authentication/form"; }
在src/main/resources下再建立一個resources目錄,用來放靜態文件。然后在resources下建立login.html頁面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登錄</title> </head> <body> <h2>標准登錄頁面</h2> <h3>表單登錄</h3> <form action="/authentication/form" method="post"> <table> <tr> <td>用戶名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密碼:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"> <button type="submit">登錄</button> </td> </tr> </table> </form> </body> </html>
建立配置類(方便服務調用者自定義配置):
/** * security相關配置(不能取名SecurityProperties,因為security中也有這個類名,不然會報錯) */ @Getter @Setter @ConfigurationProperties(prefix = "security") public class SecurityProperty { private BrowserProperty browser = new BrowserProperty(); }
/** * security-瀏覽器相關配置 */ @Getter @Setter public class BrowserProperty { private String loginPage = "/login.html"; // 登錄頁 }
修改下SecurityConfig類中的配置:
@Autowired private SecurityProperty securityProperty; @Override protected void configure(HttpSecurity http) throws Exception { // formLogin()是默認的登錄表單頁,如果不配置 loginPage(url),則使用 spring security // 默認的登錄頁,如果配置了 loginPage()則使用自定義的登錄頁 http.formLogin() // 表單登錄 .loginPage(SecurityConst.AUTH_REQUIRE) .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登錄請求攔截的url,也就是form表單提交時指定的action .and() .authorizeRequests() // 對請求授權 .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允許所有人訪問login.html和自定義的登錄頁 .anyRequest() // 任何請求 .authenticated()// 需要身份認證 .and() .csrf().disable() // 關閉跨站偽造 ; }
配置代碼分析:
從上面可以看出,當第一次請求時(如http://localhost:18081/user),會先跳轉到loginPage配置的路徑里,即/authentication/require
/authentication/require與/login.html這兩個請求不需要認證授權(第二個url是登錄頁,可以在調用方自定義配置的)
接下來,建立一個Controller,用來處理/authentication/require請求:
@Slf4j @RestController public class SecurityController { private RequestCache requestCache = new HttpSessionRequestCache(); // 請求緩存 private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();// 頁面跳轉 @Autowired private SecurityProperty securityProperty; /** * 當需要身份認證時,跳轉到這里 (如果是html請求,則跳轉到登錄頁,否則返回401狀態碼和錯誤信息) */ @SneakyThrows @RequestMapping(SecurityConst.AUTH_REQUIRE) @ResponseStatus(code = HttpStatus.UNAUTHORIZED) // 未授權狀態碼401 public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) { // 從session中取之前緩存的請求 SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); log.info("引發跳轉的請求是:" + targetUrl); if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { // 跳轉到指定的頁面 redirectStrategy.sendRedirect(request, response, securityProperty.getBrowser().getLoginPage()); } } return new SimpleResponse("訪問的服務需要身份認證,請引導用戶到登錄頁"); } }
/** * 響應 */ @Getter @Setter @AllArgsConstructor public class SimpleResponse { private Object content; }
RequestCache接口聲明了緩存與恢復操作。默認實現類是HttpSessionRequestCache,接口的聲明如下:
public interface RequestCache { // 將request緩存到session中 void saveRequest(HttpServletRequest request, HttpServletResponse response); // 從session中取request SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response); // 獲得與當前request匹配的緩存,並將匹配的request從session中刪除 HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response); // 刪除緩存的request void removeRequest(HttpServletRequest request, HttpServletResponse response); }
對request的緩存,可參考Spring Security 源碼解析(一)
建立好之后的文件目錄如下所示:
spring-security-demo
在src/main/resources下再建立一個resources目錄,用來放靜態文件。然后在resources下建立plogin.html頁面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>登錄</title> </head> <body> <h2>自定義登錄頁面</h2> </body> </html>
啟動服務,訪問 http://localhost:18081/user,跳轉到如下頁面:
可以看到,訪問/user時,需要授權認證,然后跳轉到/authentication/require,直接返回401狀態碼和我們自定義的content數據
訪問 http://localhost:18081/index.html,跳轉到如下頁面:
可以看到,訪問 /index.html 時,需要授權認證,然后跳轉到 /authentication/require,發現是html請求,重定向到 login.html 頁面
接下面,在服務調用者的配置文件application.yml中,加上自定義的登錄頁配置:
security:
browser:
loginPage: /plogin.html
然后重啟項目,訪問http://localhost:18081/index.html,跳轉到如下頁面:
可以看到,現在跳轉到了自定義的登錄頁面