參考教程:陳木鑫老師的《Spring Security 實戰》html
經過Intellij IDEA建立Spring Boot項目的方式有許多種,其中最簡單的方式就是使用Spring Initializr
工具。
Spring Initializr 容許咱們提早選定一些經常使用的項目依賴,此處咱們選擇 Security 做爲構建Spring
Security項目的最小依賴,選擇Web做爲Spring Boot構建Web應用的核心依賴。
Next :
Next:
建立項目的目錄結構:前端
在自動構建的Spring Security 項目中,Spring Initializr 爲咱們引入瞭如下依賴:java
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
咱們點開spring-boot-starter-security
能夠看到,其包含了如下依賴:web
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <scope>compile</scope> </dependency>
其中 spring-security-web
和spring-security-config
兩個核心模塊,正是官方建議引入的Spring Security最小依賴。spring
在項目中聲明一個測試路由TestController
:後端
package com.haan.springsecuritydemo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping public String hello(){ return "Hello Spring Security!"; } }
SpringsecuritydemoApplication
運行SpringsecuritydemoApplication
,默認啓動 8080
端口,打開瀏覽器,訪問localhost:8080
,咱們發現頁面跳轉到了localhost:8080/login
:
在引入Spring Security項目以後,雖然沒有進行任何相關的配置或編碼,但Spring Security有一個默認的運行狀態,要求在通過表單基本認證後才能訪問對應的URL資源,其默認使用的用戶名爲 user
,密碼則是動態生成並打印到控制檯的一串隨機碼。翻看控制檯的打印信息:
輸入用戶名和密碼後,單擊「登陸」按鈕便可成功訪問:瀏覽器
基本表單認證中,用戶名和密碼都是能夠配置的,最多見的就是在resources下的application
配置文件中修改:安全
spring.security.user.name=user02 spring.security.user.password=aaaaaa
從新啓動程序,發現控制檯再也不打印默認密碼串了,此時使用咱們自定義的用戶名和密碼便可登陸。服務器
protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); ((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic(); }
能夠看到 WebSecurityConfigurerAdapter
已經默認聲明瞭一些安全特性:app
spring boot 默認定義了DefaultConfigurerAdapter
,由@ConditionalOnMissingBean
可知當沒有其餘WebSecurityConfigurerAdapter
被定義時,將使用DefaultConfigurerAdapter
:
@Configuration( proxyBeanMethods = false ) @ConditionalOnClass({WebSecurityConfigurerAdapter.class}) @ConditionalOnMissingBean({WebSecurityConfigurerAdapter.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) public class SpringBootWebSecurityConfiguration { public SpringBootWebSecurityConfiguration() { } @Configuration( proxyBeanMethods = false ) @Order(2147483642) static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter { DefaultConfigurerAdapter() { } } }
Spring boot提供了WebSecurityConfigurerAdapter
的默認實現DefaultConfigurerAdapter
,可以提供基本表單登陸認證。
雖然自動生成的表單登陸頁能夠方便、快速地啓動,可是大多數應用程序更但願提供本身的表單登陸頁,此時就須要咱們提供本身的WebSecurityConfigurerAdapter
來代替DefaultConfigurerAdapter
,覆寫WebSecurityConfigurerAdapter
的configure
方法:
@EnableWebSecurity public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") //指明登陸頁面 .permitAll() //指明登陸頁容許全部進行訪問 .and() .csrf().disable(); } }
authorizeRequests()
方法實際上返回了一個 URL 攔截註冊器,咱們能夠調用它提供的anyRequest()
、antMatchers()
和 regexMatchers()
等方法來匹配系統的URL,併爲其指定安全策略。formLogin()
方法和httpBasic()
方法都聲明瞭須要Spring Security提供的表單認證方式,分別返回對應的配置器。其中formLogin().loginPage("/myLogin.html")
指定自定義的登陸頁 /myLogin.html
,同時,Spring Security會用/myLogin.html
註冊一個POST
路由,用於接收登陸請求。
csrf()
方法是Spring Security提供的跨站請求僞造防禦功能,當咱們繼承WebSecurityConfigurer Adapter
時會默認開啓 csrf()
方法。訪問localhost:8080
,咱們發現,頁面就跳轉到了localhost:8080/myLogin.html
,因爲咱們靜態文件中並無myLogin.html
文件,因此提示了一個404的white page
:
咱們在resources/static
文件夾下建立頁面myLogin.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello Security!</title> </head> <body> <h3>登陸頁</h3> <form action="/myLogin.html" method="post"> <input type="text" name="username"> <br/> <input type="text" name="password"> <br/> <input type="submit" value="login"> </form> </body> </html>
重啓服務,再次訪問localhost:8080
:
輸入上面的application.properties
中配置的用戶登陸帳戶和密碼進行登陸,登錄成功:
在自定義表單登陸頁以後,處理登陸請求的URL
也會相應改變,默認狀況下,
若是隻配置loginPage
而不配置loginProcessingUrl
的話那麼loginProcessingUrl
默認就是loginPage
,若是須要自定義登陸請求的URL
,須要配置loginProcessingUrl
:
重啓登陸,咱們發現中間訪問了localhost:8080/myLogin
補充:
loginPage
和loginProcessingUrl
- 二者都不配置:默認都是
/login
- 二者都配置:按本身的來
- 只配置
loginProcessingUrl
:loginPage
默認/login
- 只配置
loginPage
:loginProcessingUrl
默認就是loginPage
此時,有些讀者可能會有疑問,由於按照慣例,在發送登陸請求並認證成功以後,頁面會跳轉回原訪問頁。在某些系統中的確是跳轉回原訪問頁的,但在部分先後端徹底分離、僅靠JSON完成全部交互的系統中,通常會在登陸時返回一段 JSON 數據,告知前端成功登陸成功與否,由前端決定如何處
理後續邏輯,而非由服務器主動執行頁面跳轉。這在Spring Security中一樣能夠實現。
表單登陸配置模塊提供了 successHandler()
和 failureHandler()
兩個方法,分別處理登陸成功和登陸失敗的邏輯。
successHandler()
方法帶有一個Authentication
參數,攜帶當前登陸用戶名及其角色等信息;failureHandler()
方法攜帶一個AuthenticationException
異常參數。具體處理方式需按照系統的狀況自定義。@EnableWebSecurity public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/myLogin.html") //指明登陸頁面 .loginProcessingUrl("/myLogin") //指明處理登錄的URL路徑,即登錄表單提交請求 .successHandler(new AuthenticationSuccessHandler() { // 設置登陸成功的處理器 @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { PrintWriter responseWriter = httpServletResponse.getWriter(); String name = authentication.getName(); responseWriter.write(name+" login success!"); } }) .failureHandler(new AuthenticationFailureHandler() { // 設置登陸失敗的處理器 @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { PrintWriter responseWriter = httpServletResponse.getWriter(); responseWriter.write("login error!"); } }) .permitAll() //指明登陸頁容許全部進行訪問 .and() .csrf().disable(); } }
正確的帳號密碼:
錯誤的帳號密碼: