1.定義本身的登陸頁面
咱們須要根據本身的業務系統構建本身的登陸頁面以及登陸成功、失敗處理
在spring security提供給個人登陸頁面中,只有用戶名、密碼框,而自帶的登陸成功頁面是空白頁面(能夠重定向以前請求的路徑中),而登陸失敗時也只是提示用戶被鎖定、過時等信息。html
在實際的開發中,則須要更精細力度的登陸控制,記錄錯誤的日誌(錯誤的次數等)spring
2.自定義登陸頁面json
http.formLogin() .loginPage("/sign.html") //位於resources/resources/sign.html .and() .authorizeRequests() .anyRequest().authenticated();
頁面內容以下:瀏覽器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sign.html</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>
啓動以後,訪問咱們的路徑:http://localhost:8080/sign.html
這個時候瀏覽器會報錯:重定向的次數過多,這是爲何呢?
由於在配置中,咱們只是配置了loginPage("/sign.html"),可是又沒有給該請求作受權,又由於沒有作受權,又被跳轉給sign.html頁面,因此會是:重定向的次數過多。
此時:須要再配置受權路徑,改造以後緩存
http.formLogin() .loginPage("/sign.html") .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() //當訪問sign.html這個頁面的時候不須要進行身份認證 .anyRequest().authenticated();
在sign.html中,本身配置了一個post路徑,在spring security原理中,表單登陸其實是由UsernamePasswordAuthenticationFilter這個過濾器來處理的,在這個過濾器中
處理的是/login 請求,爲了讓UsernamePasswordAuthenticationFilter這個過濾器知道處理咱們自定義的登陸路徑/authentication/form,還須要再配置登陸的處理請求安全
http.formLogin() .loginPage("/sign.html") .loginProcessingUrl("/authentication/form") //登陸請求 .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() .anyRequest().authenticated();
啓動以後,繼續訪問咱們的路徑:http://localhost:8080/sign.html
輸入帳戶名和密碼,登陸以後會報錯,403app
在默認狀況下,spring security提供了跨站請求僞造的防禦,用CSRF Token來完成的,在***和防禦的時候再細講,目前先把跨站請求僞造的功能先disable掉。ide
http.formLogin() .loginPage("/sign.html") .loginProcessingUrl("/authentication/form") //登陸請求 .and() .authorizeRequests() .antMatchers("/sign.html").permitAll() .anyRequest().authenticated() .and() .csrf().disable();
啓動以後,繼續訪問咱們的路徑:http://localhost:8080/user/1 ,系統會幫咱們重定向到sign.html頁面
此時咱們輸入正確的用戶名和密碼就能夠訪問咱們的請求了post
3.優化rest請求和html請求
完成了基本功能以後還須要繼續優化咱們的代碼結構,是要面向可重用的一種。
目前有2個問題:優化
當咱們接收到html請求或者數據請求的時候,先判斷是否須要身份認證(spring security來作的)若是是否的話就直接返回了,若是是的話則須要跳轉到咱們自定義的Controller上面去(目前咱們的作法是跳轉到了sign.html頁面上)在該方法內判斷是html請求仍是數據請求。
Controller
@RestController public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(BrowserSecurityConfig.class); //拿到引起跳轉的請求(HttpSessionRequestCache把當前的請求緩存到Session中) private RequestCache requestCache = new HttpSessionRequestCache(); private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Autowired private SecurityProperties securityProperties; //當須要身份認證時,跳轉到這裏 @RequestMapping("/authentication/require") @ResponseStatus(code=HttpStatus.UNAUTHORIZED) //不是html請求時,返回401狀態碼 public SimpleResponse requireAuthentication(HttpServletRequest request,HttpServletResponse response) throws IOException { //以前緩存的請求(能夠拿到引起跳轉的請求) SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest != null) { String targetUrl = savedRequest.getRedirectUrl(); logger.info("引起跳轉的請求是:"+targetUrl); //是否以.html結尾,若是是則跳轉到登陸頁面 if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { //這個url,咱們須要作成可配置的url(由於咱們不可能每次都跳轉到咱們本身的寫的固定登陸頁面,須要根據每一個項目的不一樣) //這個時候就須要用到**Properties 配置文件類來作靈活性配置 redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); } //若是不是html請求,則返回401狀態碼以及錯誤信息 } return new SimpleResponse("訪問的服務須要身份認證,請引導用戶到登陸頁"); } }
SecurityProperties自定義的登陸頁配置屬性類,爲了可配置化
#另外的項目的登陸請求的頁面 core.security.browser.loginPage = /demo-sign.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-sign</title> </head> <body> <h2>demo-sign自定義登陸頁</h2> </body> </html>
而咱們不想單純的要一個簡單的配置是,而是可管理的配置類,由於後面會有其餘的配置,例如驗證碼的配置,OAuth的配置,等 這個時候須要同一個的配置類入口(SecurityProperties)
//讀取配置文件內的信息 @ConfigurationProperties(prefix="core.security") public class SecurityProperties { private BrowserProperties browser = new BrowserProperties(); public BrowserProperties getBrowser() { return browser; } public void setBrowser(BrowserProperties browser) { this.browser = browser; } } public class BrowserProperties { //標準的登陸頁面,若是其餘項目沒有配置則使用默認的登陸配置 private String loginPage = "/sign.html"; public String getLoginPage() { return loginPage; } public void setLoginPage(String loginPage) { this.loginPage = loginPage; } } //爲了使core.security生效則須要一個@Configuration類 @Configuration @EnableConfigurationProperties(SecurityProperties.class) public class SecurityCoreConfig { } //最後要在權限配置類**BrowserSecurityConfig**中 配置放行的url private final static String loginPage = "/authentication/require"; @Autowired private SecurityProperties securityProperties; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage(loginPage) .loginProcessingUrl("/authentication/form") .and() .authorizeRequests() .antMatchers(loginPage).permitAll() //自定義的登陸頁面權限放開 .antMatchers(securityProperties.getBrowser().getLoginPage()).permitAll() .anyRequest().authenticated() .and() .csrf().disable(); }
第一種狀況:訪問請求:http://localhost:8080/user/1
第二種狀況:訪問請求:http://localhost:8080/index.html
第三種狀況:關閉配置,繼續訪問請求:http://localhost:8080/index.html
#core.security.browser.loginPage = /demo-sign.html
這個功能就是咱們目前想要的,能夠針對不一樣的請求對於沒有權限時的攔截以及調整判斷。