1. 程式人生 > 實用技巧 >【SpringSecurity】01 授權、認證、登出

【SpringSecurity】01 授權、認證、登出

【前提情要】

Security學習地址:

https://www.bilibili.com/video/BV1KE411i7bC

狂神的微信筆記:

https://mp.weixin.qq.com/s?__biz=Mzg2NTAzMTExNg==&mid=2247483957&idx=1&sn=fc30511490b160cd1519e7a7ee3d4ed0&chksm=ce610496f9168d8082bf6cb2e54b0b8628a1db596c1d297d06e756e621569dc3047334b3062c&mpshare=1&scene=23&srcid=0729Qvgf1Lb6AstEnrABaRiH&sharer_sharetime=1595987265426&sharer_shareid=04a2988e19defb0bbbb3d27a02ec4289#rd

【演示案例搭建】

首先是新建一個SpringBoot工程,

不要勾選任何元件,直接新增依賴即可:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</
artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

把靜態資原始檔和配置匯入資源目錄中:

每個Level目錄中的頁面都一樣的【空的,啥都沒有】,只是為了代表不同的可訪問級別的資源

首頁展示了所有可以訪問的內容:

然後這是登陸頁面:

編寫一個路由控制器,實現頁面之間的跳轉訪問:

package cn.echo42.controller;

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

/**
 * @author DaiZhiZhou
 * @file SpringSecurity
 * @create 2020-07-29 9:53
 */

@Controller
public class RouterController {

    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }

    @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;
    }

}

在我們啟動工程之後,所有的頁面毫無阻攔的就可以被所有的訪問者訪問到

使用者的隱私資料就可以被攻擊者竊取。簡而言之就是安全隱患

從這裡引入安全的概念,然後就是SpringSecurity這樣一個解決方案:

Spring Security是一個功能強大且高度可定製的身份驗證和訪問控制框架。
它實際上是保護基於spring的應用程式的標準。 Spring Security是一個框架,側重於為Java應用程式提供身份驗證和授權。
與所有Spring專案一樣,Spring安全性的真正強大之處在於它可以輕鬆地擴充套件以滿足定製需求 從官網的介紹中可以知道這是一個許可權框架。
想我們之前做專案是沒有使用框架是怎麼控制權限的?
對於許可權 一般會細分為功能許可權,訪問許可權,和選單許可權。
程式碼會寫的非常的繁瑣,冗餘。 怎麼解決之前寫許可權程式碼繁瑣,冗餘的問題,
一些主流框架就應運而生而Spring Scecurity就是其中的一種。

首先引入安全框架元件座標:

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

我們之前編寫的內容不需要做出任何的改動:

【授權authorize】

編寫一個SpringSecurity配置類:

package cn.echo42.config;

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;

/**
 * @author DaiZhiZhou
 * @file SpringSecurity
 * @create 2020-07-29 10:34
 */

@EnableWebSecurity // 註冊為WebSecurity的一個Bean
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override // 注意重寫的是這個HttpSecurity引數的configure方法
    protected void configure(HttpSecurity http) throws Exception {
        // 首頁允許所有人訪問,那些LEVEL頁面只有對應具備的許可權的使用者才可以訪問
        
        http.
                authorizeRequests(). // 對以下請求進行授權
                antMatchers("/"). // /表示我們的首頁
                permitAll(). // 表示完全允許訪問
                
                // 對相應的地址進行相應的角色設定
                antMatchers("/level1/**").hasRole("vip1"). 
                antMatchers("/level2/**").hasRole("vip2").
                antMatchers("/level3/**").hasRole("vip3");
    }
}

這時候執行工程:

雖然首頁所有人均可以訪問,但是點選下面的這些LEVEL頁面就會被拒絕

There was an unexpected error (type=Forbidden, status=403).

訪問被禁止了,也就是我們Security起效了

但是如果訪問禁止,按照業務流程,我們應該是將這個請求返回到登入頁面:

所以需要進行下一步的設定:

        // 沒有許可權,重定向到登入頁面
        http.formLogin();

這時候再來訪問就會跳轉到登入頁面:

不過這登陸頁面並不是我們自己那個素材的登入頁,而是Security提供的

【許可權認證】

重寫 過載的config方法,是引數為AuthenticationManagerBuilder的方法

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

但是我們這個案例沒有涉及資料庫,所以使用的是從記憶體中獲取資料用以認證

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        
        auth.inMemoryAuthentication().
                // 認證角色  使用者名稱稱、密碼、對應的多個角色
                        
                // 超級管理員就直接賦值所有角色即可        
                withUser("admin").password("admin").roles("vip1","vip2","vip3").
                // 如果是多個角色則使用and方法加上
                and().withUser("user1").password("123").roles("vip1").
                and().withUser("user2").password("123").roles("vip2").
                and().withUser("user3").password("123").roles("vip3");
    }

然後再次訪問:

當訪問被拒絕重定向到登入頁面

然後輸入我們在Security設定的許可權資訊之後。。。

我這裡看不出來,只有一個500,Security的要求意思是說,密碼沒有加密也不能訪問

所以我們需要做的是就是對密碼再加密即可:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).
                // 認證角色  使用者名稱稱、密碼、對應的多個角色

                // 超級管理員就直接賦值所有角色即可
                withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("vip1","vip2","vip3").
                // 如果是多個角色則使用and方法加上
                and().withUser("user1").password(new BCryptPasswordEncoder().encode("123")).roles("vip1").
                and().withUser("user2").password(new BCryptPasswordEncoder().encode("123")).roles("vip2").
                and().withUser("user3").password(new BCryptPasswordEncoder().encode("123")).roles("vip3");
    }

再次訪問:

使用user1訪問level1目錄的資源是可以的

但是訪問level2就沒辦法了

【登出功能】

    @Override // 注意重寫的是這個HttpSecurity引數的configure方法
    protected void configure(HttpSecurity http) throws Exception {
        // 首頁允許所有人訪問,那些LEVEL頁面只有對應具備的許可權的使用者才可以訪問

        http.
                authorizeRequests(). // 對以下請求進行授權
                antMatchers("/"). // /表示我們的首頁
                permitAll(). // 表示完全允許訪問

                // 對相應的地址進行相應的角色設定
                antMatchers("/level1/**").hasRole("vip1").
                antMatchers("/level2/**").hasRole("vip2").
                antMatchers("/level3/**").hasRole("vip3");

        // 沒有許可權,重定向到登入頁面
        http.formLogin();
        
        // 登出,退出
        http.logout();
    }

在首頁中插入一個登出標籤:

<a class="item" th:href="@{/logout}">
    <i class="sign-out icon"></i> 登出
</a>

測試退出:

點選之後自動回到登入頁面,並且標註了是登出回到登入頁的

如果不是跳轉到登入頁,而是首頁,則可以:

http.logout().logoutSuccessUrl("/");

即追加一個登出成功的重定向地址