天天看點

SpringBoot———安全架構之Shiro一、簡介二、功能三、架構四、認證流程五、實作步驟六、整體項目

SpringBoot擴充——Shiro

  • 一、簡介
  • 二、功能
  • 三、架構
    • Subject——主體
    • SecurityManager——安全管理器
        • Authenricator——認證器
        • Authorizer——授權器
        • SessionManager——會話管理器
        • SessionDao——會話DAO
        • CacheManager——緩存控制器
    • Realm——領域
    • Cryptography——密碼管理器
  • 四、認證流程
  • 五、實作步驟
      • 案例一:搭建環境,進行測試
      • 案例二:添加登入攔截權限
      • 案例三:實作使用者認證功能
      • 案例四:整合MyBatis,連接配接資料庫
      • 案例五:實作使用者權限功能
      • 案例六:實作使用者登出功能
      • 案例七:實作部分顯示功能
  • 六、整體項目

一、簡介

  • Apache Shiro是一個強大且易用的Java安全架構,執行身份驗證、授權、密碼和會話管理。使用Shiro的易于了解的API,您可以快速、輕松地獲得任何應用程式,從最小的移動應用程式到最大的網絡和企業應用程式。
與SpringSecurity的差別
  • shiro:靈活性強,易學易擴充。同時,不僅可以在web中使用,可以工作在任務環境内中。
  • security:靈活性較差,但與spring整合性好。
  • 應用環境:不依賴于任何架構,可在JavaSE中使用,更常見的是JavaEE中。
  • 功能:管理應用程式中與安全相關的全部,同時盡可能支援多種實作方法。Shiro是建立在完善的接口驅動設計和面向對象原則之上的,支援各種自定義行為。

二、功能

SpringBoot———安全架構之Shiro一、簡介二、功能三、架構四、認證流程五、實作步驟六、整體項目

Shiro不會去維護使用者、維護權限,需要自己設計,通過相應的接口注入給Shiro。

四大基石----身份驗證,授權,會話管理,加密

  1. Authentication:使用者認證,驗證使用者是不是擁有相應的身份。
  2. Authorization:使用者授權,驗證某個已認證的使用者是否擁有某個權限;即判斷使用者是否能做事情。
  3. Session Manager:會話管理,即使用者登入後就是一次會話,在沒有退出之前,它的所有資訊都在會話中。
  4. Cryptography:加密,保護資料的安全性,如密碼加密存儲到資料庫,而不是明文存儲。

Shiro擴充功能

  1. Web Support:Web支援,可以非常容易的內建到Web環境。
  2. Caching:緩存,比如使用者登入後,其使用者資訊、擁有的角色/權限不必每次去查,這樣可以提高效率。
  3. Concurrency:shiro支援多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去。
  4. Testing:提供測試支援。
  5. Run As:允許一個使用者假裝為另一個使用者(如果他們允許)的身份進行通路。
  6. Remember Me:記住我。

Shiro嘗試在任何應用環境下實作這些功能,而不依賴其他架構、容器或應用伺服器。

三、架構

SpringBoot———安全架構之Shiro一、簡介二、功能三、架構四、認證流程五、實作步驟六、整體項目

Shiro 有三大核心元件,即 Subject、SecurityManager 和 Realm。

Subject——主體

  • 外部應用于subject進行互動,subject記錄了目前的操作方————記為主體。可以是使用者或者網絡爬蟲。
  • 作用:主體需要通路系統,系統則對其進行認證和授權
  • 結構:包含Principals 和 Credentials 兩個資訊

Principals:代表身份。可 以是使用者名、郵件、手機号碼等等,用來辨別一個登入主體的身份。

Credentials:代表憑證。常見的有密碼,數字證書等等。

外部程式通過subject(使用者)進行認證授權,而subject是通過SecurityManager安全管理器進行認證授權,即:

Subject代表了目前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。

SecurityManager——安全管理器

  • Shiro架構的核心。典型的Facade模式
  • 作用:Shiro通過SecurityManager來管理内部元件執行個體,并通過它來提供安全管理的各種服務(認證和授權)。
  • 結構:SecurityManager是一個接口,繼承了Authenticator, Authorizer, SessionManager三個接口。
原理:SecurityManager是通過Authenticator進行認證,通過Authorizer進行授權,通過SessionManager進行會話管理等。

開發人員将大部分精力放在了 Subject 認證主體上,與 Subject 互動背後的安全操作,則由 SecurityManager 來完成。

Authenricator——認證器

  • 作用:對Subject進行認證。Subject的資訊在shrio中是通過AuthenticationToken對象來儲存,由AuthenricationStrategy進行驗證管理。
  • Authenticator是一個接口,通過ModularRealmAuthenticator實作類基本上可以滿足大多數需求,也可以自定義認證器。

Authorizer——授權器

  • 作用:Subject認證後,授權器來對其授予對應角色權限。即控制着使用者能通路應用中的哪些功能。

SessionManager——會話管理器

  • 作用:Shiro架構定義了一套會話管理,它不依賴web容器的session
  • 優點:Shiro可以使用在非web應用上,也可以将分布式應用的會話集中在一點管理,此特性可使它實作單點登入。

    可以了解為:Shiro抽象了一個自己的Session來管理主體與應用之間互動的資料。

SessionDao——會話DAO

  • Session的接口,Shiro通過它來管理session資料。
  • 作用:個性化的session資料儲存需要使用sessionDao。

CacheManager——緩存控制器

  • 作用:主要對Session資料和使用者權限資料進行緩存,減小資料庫的通路壓力。

Realm——領域

  • 相當于DataSource資料源。
  • 作用:securityManager進行安全認證需要通過Realm擷取使用者權限資料。比如:如果使用者身份資料在資料庫那麼realm就需要從資料庫擷取使用者身份資訊。
  • 原理:需繼承AuthorizingRealm接口。至少有一個——用于認證和授權,也可自定義多個。
Realm充當了Shiro與應用安全資料間的“橋梁”或者“連接配接器”。
  • 例如:當對使用者執行認證(登入)和授權(通路控制)驗證時,Shiro會從應用配置的Realm中查找使用者及其權限資訊。

Shiro不知道使用者或權限存儲在哪及以何種格式存儲,是以一般在應用中都需要實作自己的Realm密碼子產品

注意:Subject進行認證和授權都需要調用realm,是以realm不僅僅相當于資料源,還包含了認證和授權的一種邏輯。

  • 實質:Realm是一個安全相關的DAO,封裝了資料源的連接配接細節,并在需要時将相關資料提供給Shiro。

Cryptography——密碼管理器

  • 作用:提供了一套加密/解密的元件。比如常用的散列,加/解密等功能。

日常練習所使用的md5算法其實是一種雜湊演算法,隻能加密,不能解密。

總結:Shrio的核心部分還是認證和授權部分,其他都是圍繞這倆部分進行的。

四、認證流程

  1. 應用程式代碼調用 Subject.login(token) 方法,傳入代表最終使用者身份的 AuthenticationToken 執行個體 Token。
  2. 将 Subject 執行個體委托給應用程式的 SecurityManager,開始真正的認證工作。
  3. SecurityManager 根據注入的 Realm ,讓SecurityManager得到合法的使用者和權限進行判斷和安全認證。(Realm可自定義)

五、實作步驟

案例一:搭建環境,進行測試

  1. 建立SpringBoot項目,添加web架構,導入thymeleaf、Shiro依賴。
<!--thymeleaf-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--Shiro-Spring-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.6.0</version>
</dependency>
           
  1. 在templates下建立index首頁,編寫controller,進行測試。
@Controller
public class testController {

    @RequestMapping("/index")
    public String toIndex(Model model){
        model.addAttribute("msg","這是一個Shiro的測試案例");
        return "index";
    }
}
           
  1. config包下自定義UserRealm
package com.zy.Config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class UserRealm extends AuthorizingRealm {
    /*使用者授權*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("調用了=========使用者授權方法");
        return null;
    }
    /*使用者認證*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("調用了=========使用者認證方法");
        return null;
    }
}
           
  1. 編寫ShrioConfiguration配置類
作用:裝配ShiroFilterFactoryBean,DefaultWebSecurityManager和自定義的UserRealm
package com.zy.Config;

import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShrioConfiguration {

    // 裝配ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 設定安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    // 裝配SecurityManager,注入自定義的UserRealm
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 關聯自定義的UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    // 裝配Realm
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
}
           
  1. 配置controller
@Controller
public class testController {
    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "/user/update";
    }
    @RequestMapping("/user/show")
    public String show(){
        return "/user/show";
    }
}
           
  1. 編寫前端網頁,進行測試
    SpringBoot———安全架構之Shiro一、簡介二、功能三、架構四、認證流程五、實作步驟六、整體項目
index.html
<body th:align="center">
<h1>首頁</h1>
<h2 th:text="${msg}"></h2>
<hr>
<a th:href="@{/user/show}">展示頁面</a> |
<a th:href="@{/user/add}">添加頁面</a> |
<a th:href="@{/user/update}">修改頁面</a>
</body>
           
add.html
<body align="center">
<h2>add頁面</h2>
</body>
           
update.html
<body align="center">
<h2>update頁面</h2>
</body>
           
show.html
<body align="center">
<h2>show頁面</h2>
</body>
           

總結:三個網頁均可進入。

案例二:添加登入攔截權限

  1. 在配置類的ShiroFilterFactoryBean中添權重限攔截功能
攔截的權限有:
  • anon:無需認證就可以通路
  • authc:必須認證了才能通路
  • user:必須擁有記住我功能才能使用
  • perms:擁有對某個資源的權限才能通路
  • role:擁有某個角色權限才能通路
@Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 設定安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加Shiro内置過濾器
        HashMap<String, String> map = new HashMap<>();
        map.put("/user/show","anon"); // 不用認證
        map.put("/user/add","authc");
        map.put("/user/update","authc");
        // 設定攔截權限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 攔截後跳轉到登入頁面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }
           
  1. 自定義登入頁面,配置controller
login.html
<body th:align="center">
<h1>登入</h1>
<hr>
<form method="post" action="/login">
    使用者名:<input type="text" name="username">
    密碼:<input type="password" name="pwd">
    <input type="submit" value="送出">
</form>
</body>
           
@RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
           

總結:show網頁不用登入即可檢視,add、update網頁會進入登入網頁。

案例三:實作使用者認證功能

使用者認證步驟:
    1. 擷取目前使用者
    1. 封裝使用者登入資訊生成Token
    1. 執行登入操作,可自定義捕獲異常
  1. controller中配置login
@RequestMapping("/login")
    public String login(String username,String pwd,Model model){
        // 1. 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        // 2. 封裝使用者資訊
        UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
        try{
            // 3. 執行登入操作 成功後傳回首頁
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){
            // 捕獲異常
            model.addAttribute("msg","登陸失敗!使用者名不存在!!");
            return "login";
        }catch (IncorrectCredentialsException e){
            // 捕獲異常
            model.addAttribute("msg","登陸失敗!密碼錯誤!!");
            return "login";
        }
    }
           
  1. login.html中添加msg顯示
  1. UserRealm中編寫認證檢查邏輯
/*使用者認證*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("調用了=========使用者認證方法");
        // 自定義使用者 也可從資料庫中查詢
        String username = "root";
        String pwd = "123";
        // 擷取Token————controller中使用後,全局可用
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 判斷使用者名
        if(!username.equals(token.getUsername())){
            // 若不一緻,自動抛出異常
            return null;
        }
        // Shiro幫我們判斷密碼
        return new SimpleAuthenticationInfo("",pwd,"");
    }
           

總結:擷取表單中的使用者資訊,與設定好的資訊進行比對,一緻即可認證成功,成功登陸。、

僅用比對使用者名,密碼Shiro會幫我們自動比對。

案例四:整合MyBatis,連接配接資料庫

  1. 導入mysql連接配接、Druid、MyBatis-Spring等jar包
<!--資料庫連接配接-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<!--Log4J-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!--Druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>
<!--MyBatis-Spring-starter-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>
           
  1. 編寫application.yaml和application.properties配置檔案
application.properties
mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
           
application.yaml
spring:
  datasource:
    username: root
    password: zy123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
           
  1. 編寫實體類pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}
           
  1. 編寫Dao層UserMapper,UserMapper.xml
@Repository
@Mapper
public interface UserMapper {
    User getUserByName(@Param("name") String name);
}
           
  1. 編寫service層 UserService,UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}
           
  1. UserRealm中查詢資料庫擷取使用者
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
     System.out.println("調用了=========使用者認證方法");
     // 1. 擷取Token————controller中使用後,全局可用
     UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

     // 2. 資料庫中查詢是否存在
     User user = userService.getUserByName(token.getUsername());
     if (user!=null){
            // 3. Shiro幫我們判斷密碼
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
     }
     // 使用者不存在,自動抛出異常
     return null;
}
           

總結:從資料庫中查詢使用者資料,與登入的資料進行比對,一緻即可認證成功。

案例五:實作使用者權限功能

資料庫中user表添加perms字段,用于設定權限

  1. ShrioConfiguration中 更改權限設定
HashMap<String, String> map = new HashMap<>();
        map.put("/user/update","perms[user:update]");// perms值為user:update 可進入更新頁面
        map.put("/user/add","perms[user:add]");//  perms值為user:add 可進入添加頁面
        map.put("/user/show","authc"); // 登入檢視
        // 設定攔截權限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 攔截後跳轉到登入頁面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        // 沒有權限後顯示  使用者被攔截
        shiroFilterFactoryBean.setUnauthorizedUrl("/error401");
        return shiroFilterFactoryBean;
           
  1. 設定未授權響應字元串即controller通路路徑
@RequestMapping("/error401")
	@ResponseBody
	public String error401(){
	    return "未經授權,無法通路該頁面!";
	}
           
  1. UserRealm中設定使用者授權操作
/*使用者授權*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("調用了=========使用者授權方法");
        // info:使用者授權
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        // 擷取使用者perms屬性的值,用于設定權限
        info.addStringPermission(user.getPerms());
        return info;
    }
           

結果:資料庫中添加perms字段,用于設定使用者權限,擁有權限的使用者才可通路,否則進入/error401,顯示沒有權限的資訊。

案例六:實作使用者登出功能

  1. controller中設定路徑
@RequestMapping("/logout")
    public String logout(HttpSession session){
        // 1. 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/toLogin";
    }
           
  1. ShrioConfiguration的map中添權重限
map.put("/logout","logout");
        // 設定攔截權限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
           
  1. index.html中添加登出連結

總結:使用者可登出。

案例七:實作部分顯示功能

  1. Shiro整合thymeleaf,導入對應jar包
<!-- Shiro整合thymeleaf-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
           
  1. 在ShrioConfiguration中裝配thymeleaf
// 裝配shiro整合thymeleaf
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
           
  1. 修改index.html

注意:導入Shiro整合thymeleaf命名空間

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
           
要求:對用權限的使用者顯示對應資訊,未登入顯示登入,已登入顯示登出
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首頁</title>
</head>
<body th:align="center">
<h1>首頁</h1>
    <!--未登入顯示登入-->
    <shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登入</a>
    </shiro:notAuthenticated>
    <!--登陸之後顯示登出-->
    <shiro:authenticated>
        <span th:text="${username}">使用者名:</span>
        <a th:href="@{/logout}">登出</a>
    </shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示頁面</a>
<div shiro:hasPermission="user:add">
     <a th:href="@{/user/add}">添加頁面</a>
</div>
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">修改頁面</a>
</div>
</body>
</html>
           

六、整體項目

項目結構

SpringBoot———安全架構之Shiro一、簡介二、功能三、架構四、認證流程五、實作步驟六、整體項目
  1. Pom.xml依賴
<dependencies>
        <!-- Shiro整合thymeleaf-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--資料庫連接配接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--Log4J-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <!--MyBatis-Spring-starter-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
        <!--Shiro-Spring-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!--thymeleaf-->
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>
           
  1. ShrioConfiguration配置檔案
@Configuration
public class ShrioConfiguration {
    // 裝配ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 設定安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加Shiro内置過濾器
         /*
            anon:無需認證就可以通路
            authc:必須認證了才能通路
            user:必須擁有記住我功能才能使用
            perms:擁有對某個資源的權限才能通路
            role:擁有某個角色權限才能通路
         */
        HashMap<String, String> map = new HashMap<>();
        map.put("/user/update","perms[user:update]");// perms值為user:update 可進入更新頁面
        map.put("/user/add","perms[user:add]");//  perms值為user:add 可進入添加頁面
        map.put("/user/show","anon"); // 直接檢視
        map.put("/logout","logout");
        // 設定攔截權限
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        // 攔截後跳轉到登入頁面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        // 沒有權限後顯示  使用者被攔截
        shiroFilterFactoryBean.setUnauthorizedUrl("/error401");

        return shiroFilterFactoryBean;
    }
    // 裝配SecurityManager,注入自定義的UserRealm
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 關聯自定義的UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    // 裝配Realm
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
    // 裝配shiro整合thymeleaf
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}
           
  1. 自定義UserRealm
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserServiceImpl userService;

    /*使用者認證*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("調用了=========使用者認證方法");
        // 1. 擷取Token————controller中使用後,全局可用
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        // 2. 資料庫中查詢是否存在
        User user = userService.getUserByName(token.getUsername());
        if (user!=null){
            // 3. Shiro幫我們判斷密碼
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
        }
        // 使用者不存在,自動抛出異常
        return null;
    }

    /*使用者授權*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("調用了=========使用者授權方法");
        // info:使用者授權
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        // 擷取使用者perms屬性的值,用于設定權限
        info.addStringPermission(user.getPerms());
        return info;
    }
}
           
  1. testController控制跳轉
@Controller
public class testController {
    @RequestMapping("/index")
    public String toIndex(Model model){
        return "index";
    }
    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "/user/update";
    }
    @RequestMapping("/user/show")
    public String show(){
        return "/user/show";
    }
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/logout")
    public String logout(HttpSession session){
        // 1. 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/toLogin";
    }
    @RequestMapping("/login")
    public String login(String username,String pwd,Model model){
        // 1. 擷取目前使用者
        Subject subject = SecurityUtils.getSubject();
        // 2. 封裝使用者資訊
        UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
        model.addAttribute("username",token.getUsername());
        try{
            // 3. 執行登入操作 成功後傳回首頁
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){
            // 捕獲異常
            model.addAttribute("msg","登陸失敗!使用者名不存在!!");
            return "login";
        }catch (IncorrectCredentialsException e){
            // 捕獲異常
            model.addAttribute("msg","登陸失敗!密碼錯誤!!");
            return "login";
        }
    }
    @RequestMapping("/error401")
    @ResponseBody
    public String error401(){
        return "未經授權,無法通路該頁面!";
    }
}
           
  1. 資料庫
User實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
    private String perms;
}
           
UserMapperDoa層
@Repository
@Mapper
public interface UserMapper {
    User getUserByName(@Param("name") String name);
}
           
UserMapper.xml具體sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.dao.UserMapper">

    <select id="getUserByName" resultType="User">
        select * from mybatis.user
        where name=#{name}
    </select>
</mapper>
           
UserService實作
public interface UserService {
    // 根據姓名查詢
    User getUserByName(String name);
}
           
UserServiceImpl實作類
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }
}
           
  1. 配置檔案
application.properties
mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
           
application.yaml
spring:
  datasource:
    username: root
    password: zy123456
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
           
  1. 前端網頁
index.html
<body th:align="center">
<h1>首頁</h1>
    <!--未登入顯示登入-->
    <shiro:notAuthenticated>
        <a th:href="@{/toLogin}">登入</a>
    </shiro:notAuthenticated>
    <!--登陸之後顯示登出-->
    <shiro:authenticated>
        使用者名:<span th:text="${username}"></span> <br> <br>
        <a th:href="@{/logout}">登出</a>
    </shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示頁面</a> <br>
<div shiro:hasPermission="user:add">
     <a th:href="@{/user/add}">添加頁面</a>
</div> <br>
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">修改頁面</a>
</div>  <br>
</body>
           
login.html
<body th:align="center">
<h1>登入</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form method="post" action="/login">
    使用者名:<input type="text" name="username">
    密碼:<input type="password" name="pwd">
    <input type="submit" value="送出">
</form>
</body>
           
add.html
<body align="center">
<h2>add頁面</h2>
</body>
           
show.html
<body align="center">
<h2>show頁面</h2>
</body>
           
update.html
<body align="center">
<h2>update頁面</h2>
</body>