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是建立在完善的接口驅動設計和面向對象原則之上的,支援各種自定義行為。
二、功能
Shiro不會去維護使用者、維護權限,需要自己設計,通過相應的接口注入給Shiro。
四大基石----身份驗證,授權,會話管理,加密
- Authentication:使用者認證,驗證使用者是不是擁有相應的身份。
- Authorization:使用者授權,驗證某個已認證的使用者是否擁有某個權限;即判斷使用者是否能做事情。
- Session Manager:會話管理,即使用者登入後就是一次會話,在沒有退出之前,它的所有資訊都在會話中。
- Cryptography:加密,保護資料的安全性,如密碼加密存儲到資料庫,而不是明文存儲。
Shiro擴充功能
- Web Support:Web支援,可以非常容易的內建到Web環境。
- Caching:緩存,比如使用者登入後,其使用者資訊、擁有的角色/權限不必每次去查,這樣可以提高效率。
- Concurrency:shiro支援多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去。
- Testing:提供測試支援。
- Run As:允許一個使用者假裝為另一個使用者(如果他們允許)的身份進行通路。
- Remember Me:記住我。
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的核心部分還是認證和授權部分,其他都是圍繞這倆部分進行的。
四、認證流程
- 應用程式代碼調用 Subject.login(token) 方法,傳入代表最終使用者身份的 AuthenticationToken 執行個體 Token。
- 将 Subject 執行個體委托給應用程式的 SecurityManager,開始真正的認證工作。
- SecurityManager 根據注入的 Realm ,讓SecurityManager得到合法的使用者和權限進行判斷和安全認證。(Realm可自定義)
五、實作步驟
案例一:搭建環境,進行測試
- 建立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>
- 在templates下建立index首頁,編寫controller,進行測試。
@Controller
public class testController {
@RequestMapping("/index")
public String toIndex(Model model){
model.addAttribute("msg","這是一個Shiro的測試案例");
return "index";
}
}
- 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;
}
}
- 編寫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();
}
}
- 配置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";
}
}
- 編寫前端網頁,進行測試
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>
總結:三個網頁均可進入。
案例二:添加登入攔截權限
- 在配置類的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;
}
- 自定義登入頁面,配置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網頁會進入登入網頁。
案例三:實作使用者認證功能
使用者認證步驟:
- 擷取目前使用者
- 封裝使用者登入資訊生成Token
- 執行登入操作,可自定義捕獲異常
- 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";
}
}
- login.html中添加msg顯示
- 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,連接配接資料庫
- 導入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>
- 編寫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
- 編寫實體類pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
- 編寫Dao層UserMapper,UserMapper.xml
@Repository
@Mapper
public interface UserMapper {
User getUserByName(@Param("name") String name);
}
- 編寫service層 UserService,UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByName(String name) {
return userMapper.getUserByName(name);
}
}
- 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字段,用于設定權限
- 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;
- 設定未授權響應字元串即controller通路路徑
@RequestMapping("/error401")
@ResponseBody
public String error401(){
return "未經授權,無法通路該頁面!";
}
- 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,顯示沒有權限的資訊。
案例六:實作使用者登出功能
- controller中設定路徑
@RequestMapping("/logout")
public String logout(HttpSession session){
// 1. 擷取目前使用者
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/toLogin";
}
- ShrioConfiguration的map中添權重限
map.put("/logout","logout");
// 設定攔截權限
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
- index.html中添加登出連結
總結:使用者可登出。
案例七:實作部分顯示功能
- Shiro整合thymeleaf,導入對應jar包
<!-- Shiro整合thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 在ShrioConfiguration中裝配thymeleaf
// 裝配shiro整合thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
- 修改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>
六、整體項目
項目結構
- 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>
- 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();
}
}
- 自定義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;
}
}
- 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 "未經授權,無法通路該頁面!";
}
}
- 資料庫
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);
}
}
- 配置檔案
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
- 前端網頁
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>