天天看點

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

系列文章

SpringBoot 01 —— HelloSpringBoot、yaml配置、資料校驗、多環境切換

SpringBoot 02 —— Web簡單探究、員工管理系統

SpringBoot 03 —— Spring Security

SpringBoot 04 —— Shiro

SpringBoot 05 —— Swagger

SpringBoot 06 —— 異步任務、郵件任務、定時任務

SpringBoot 07 —— 分布式:Dubbo+Zookeeper

文章目錄

  • 系列文章
  • 十一、Spring Security
    • 11.1、實驗環境搭建
    • 11.2、認證和授權
    • 11.3、登出和權限控制
    • 11.4、記住我和定制登入頁

十一、Spring Security

簡介

Spring Security是一個功能強大且可高度定制的關于身份驗證和通路控制的架構。 它是用于保護基于Spring的應用程式的标準。

SpringSecurity是來解決Web開發的安全性問題,就像之前我們寫過的過濾器和攔截器,而SpringSecurity就相當于過濾器的一個架構,能幫我們省略很多代碼。

比如之前我們用攔截器或者過濾器來進行通路控制,要求使用者必須登入才能進入首頁面,或者對于權限不同的使用者進入的頁面也不同等,但之前寫的代碼都較為複雜、備援,而用架構就能簡單實作。

需要記住幾個關鍵類:

  • WebSecurityConfigurerAdapter:自定義安全政策
  • AuthenticationManagerBuilder:自定義認證政策
  • @EnableWebSecurity:打開WebSecurity功能(看見@EnableXXX 就是開啟某個功能)

Spring Security的兩個主要目标就是"認證"和“授權”(也就是通路控制)。後面學習的Shiro也是一樣的。

官方文檔:https://spring.io/guides/gs/securing-web/

下面我們搭建一個正常的SpringBoot Web項目。

11.1、實驗環境搭建

1、建立SpringBoot項目,添加Web子產品,并導入Thymeleaf包。

<!--thymeleaf—— 命名空間xmlns:th="http://www.thymeleaf.org“ -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
           

2、導入靜态資源檔案(從網上找的,可以不用這一步,隻是為了讓界面好看點,現在的操作都沒涉及到security)

資源下載下傳連結

連結: https://pan.baidu.com/s/1UoUJg6CsHm5bQbouYt609A 提取碼: nv8p

CSDN:https://download.csdn.net/download/qq_39763246/15982736

需要将Thymeleaf的緩存關閉

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

3、編寫一個路由轉發的Controller,将各個頁面轉發。

package com.zcy.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
//使用者路由轉發的Controller
public class RouterController {
  	//輸入 localhost:8080或者localhost:8080/index都會跳轉到首頁
    @RequestMapping({"/index", "/"})
    public String index(){
        return "index";
    }
		
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }

    //通過Restful風格,在URL輸入 level1/1就會跳轉到1.html,輸入level2/2就跳轉到2.html
    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level1/"+id;
    }
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }
}
           

4、效果

SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

11.2、認證和授權

建立一個SecurityConfig.java

package com.zcy.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

//這裡用到了AOP,橫切的思想,不會去改變我們Controller的代碼。類似于攔截器,但更強大!
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //授權
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //鍊式程式設計

        //實作的功能:首頁index 所有人都能通路,具體的功能也level 隻能被有相應權限的人通路

        //給請求添加規則,使的通路level1下的所有請求都要有vip1權限
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        //如果沒有權限,預設跳轉至登入頁面。這是Spring Security裡内置的
        http.formLogin();

    }
    //認證(Spring Security 5.0+ 要求對密碼加密才能正常使用)
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //從記憶體中讀取使用者(提前設定好的使用者,真實開發還是從資料庫中讀取,後面Shiro再講)
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("zcy1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
                .and()
                .withUser("zcy2").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2")
                .and()
                .withUser("zcy3").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2", "vip3");

        //從資料庫讀使用者
    }
}
           

效果:

1、未登入時通路各個頁面都會跳轉到登入頁面。可以發現URL欄的請求是 /login,但我們并沒有寫這個頁面,我們的login是views/login。這個頁面是Spring Security的

http.formLogin();

中提供的。

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

http.formLogin();

的源碼注解:

SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

2、登入使用者zcy1,隻有vip1的權限,隻能通路level1下的頁面,通路其他頁面會被攔截。

SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

3、登入使用者zcy3,擁有vip1、vip2、vip3,可以通路所有頁面。

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

11.3、登出和權限控制

效果圖:

  1. 未登入時
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
  1. 登入zcy1(注意,此時我們沒有定制登入頁面,用的是security内置的,是以點選登入按鈕無效,隻能URL輸入/login)
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
  1. 登出zcy1,就傳回到首頁
  2. 登入zcy2
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

實作方法:

1、在index.html中添加登出

<!--登入登出-->
<div class="right menu">
  <!--未登入-->
  <a class="item" th:href="@{/toLogin}">
    <i class="address card icon"></i> 登入
  </a>
  
  <!-- 登出 -->
  <a class="item" th:href="@{/logout}">
    <i class="sign-out card icon"></i> 登出
  </a>
</div>
           

2、在SecurityConfig.java中添加登出

//授權
@Override
protected void configure(HttpSecurity http) throws Exception {
  //鍊式程式設計

  //實作的功能:首頁index 所有人都能通路,具體的功能也level 隻能被有相應權限的人通路

  //給請求添加規則,使的通路level1下的所有請求都要有vip1權限
  http.authorizeRequests()
    .antMatchers("/").permitAll()
    .antMatchers("/views/level1/**").hasRole("vip1")
    .antMatchers("/views/level2/**").hasRole("vip2")
    .antMatchers("/views/level3/**").hasRole("vip3");

  //如果沒有權限,預設跳轉至一個内置登入頁面/login。這是Spring Security裡内置的。
  http.formLogin(); //.loginPage("可以在這裡指定自己的登入頁面);
  
 	//關閉網站防攻擊,預設會開啟,因為我們前端用的<a>,是用get方式送出,不安全。
 	http.csrf().disable();//如果沒有這句,登出會404
  
  //開啟登出功能,logoutSuccessUrl作用是登出成功跳轉到新url,點選登出跳轉到首頁
  http.logout().logoutSuccessUrl("/");
}
           

http.logout()

源碼注釋:

SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

3、Thymeleaf整合Spring Security

我們原來定制化首頁是利用後端傳值給前端,前端進行判斷,然後顯示,例如

<div th:if="${session.user.name}">
  ...顯示專屬内容
</div>
           

現在我們在Thymeleaf中直接整合Spring Security

  • 先導包
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>
           
  • 降低Spring Boot版本至2.0.7(Thymeleaf不支援高版本去整合Security,現在一般不會去整合Security)

    降低後,會下載下傳新版本,2.0.7的版本,我們還需要改變junit的包,詳情看下面圖檔。

    SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security
  • 修改index.html
    SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

11.4、記住我和定制登入頁

1、記住我:不登出,重新進入該頁面會自動登入

SecurityConfig.java的configure方法添加代碼:

//記住我,原理:添加Cookie到浏覽器,關閉浏覽器重新進入頁面後保持登入狀态
http.rememberMe();//預設儲存兩周
           

效果:

SpringBoot 03 —— Spring Security系列文章十一、Spring Security
SpringBoot 03 —— Spring Security系列文章十一、Spring Security

2、定制登入頁

修改SecurityConfig.java的configure方法:

//如果沒有權限,預設跳轉至一個内置登入頁面/login。這是Spring Security裡内置的。
//usernameParameter和passwordParameter指定前端傳遞過來的參數名稱
//loginPage指定自己的登入頁面
http.formLogin()
  .loginPage("/toLogin")
  .usernameParameter("user")
  .passwordParameter("pwd");
//.loginProcessingUrl("/login")可指定真正的URL,即在浏覽器輸入/toLogin,但請求是/login

//記住我,原理:添加Cookie到浏覽器,關閉浏覽器重新進入頁面後保持登入狀态
//預設儲存兩周.rememberMeParameter指定前端傳遞的參數名稱
http.rememberMe()
  .rememberMeParameter("remember");
           

修改index.html,原來參數名是username和password,現在修改了。并新增一個單選框,記住我。

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

效果:首頁點選登入按鈕可以正常登入了,且具備記住我功能。

SpringBoot 03 —— Spring Security系列文章十一、Spring Security

繼續閱讀