SpringBoot整合Shiro的方法詳解
1.Shito簡介
1.1 什么是shiro
Apache Shiro是一個(gè)java安全(權(quán)限)框架
Shiro可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在javase環(huán)境,也可以用在javaee環(huán)境
shiro可以完成,認(rèn)證,授權(quán),加密,會(huì)話管理,web集成,緩存等。

1.2 有哪些功能

2.QuickStart
官網(wǎng)下載的可能慢一些
首先新建一個(gè)普通的maven項(xiàng)目,然后在項(xiàng)目中導(dǎo)入新的maven項(xiàng)目模塊,結(jié)構(gòu)如下:

然后開始創(chuàng)建我們需要的文件,這些文件都可以在官網(wǎng)下載的文件中可以找到:
shiro.ini:
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5
導(dǎo)入相關(guān)依賴 pom.xml,官網(wǎng)未給出詳細(xì)的依賴,具體的參考給出的git下載的文件,然后做了一些簡單的修改。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Shiro</artifactId>
<groupId>com.nuist</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hello-shiro</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- configure logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
log4j.prop[erties:
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
最重要的配置文件:
QuickStart:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.InitialContext;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// 舊方法,由于shiro更新無法正常使用
// Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// SecurityManager securityManager = factory.getInstance();
// 新方法 shiro更新問題 解決正常運(yùn)行
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// 獲取當(dāng)前用戶對(duì)象
Subject currentUser = SecurityUtils.getSubject();
// 通過當(dāng)前用戶拿到session
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("subject =>! [" + value + "]");
}
// 判斷當(dāng)前用戶是否被認(rèn)證
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true); // 設(shè)置記住我
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// 最重要的一個(gè)異常,認(rèn)證異常
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
// 測(cè)試一個(gè)簡單的權(quán)限
// 粗粒度的一個(gè)權(quán)限限制
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//注銷功能
currentUser.logout();
System.exit(0);
}
}
以上主要的幾個(gè)關(guān)鍵部分代碼:
對(duì)象初始化部分
// 新方法 shiro更新問題 解決正常運(yùn)行
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
獲取當(dāng)前用戶
Subject currentUser = SecurityUtils.getSubject();
判斷用戶是否被認(rèn)證
!currentUser.isAuthenticated()
判斷用戶是否具有什么角色
currentUser.hasRole("schwartz")
判斷用戶是否擁有權(quán)限
currentUser.isPermitted("lightsaber:wield")
注銷
currentUser.logout();
此時(shí)我們運(yùn)行啟動(dòng),項(xiàng)目如下,那么一個(gè)簡單的shiro quickStart 就已經(jīng)啟動(dòng)好了。

注意
shir最重要的三個(gè)部分:
- subject 用戶
- SecurityManager 管理所有的用戶
- Realm 連接數(shù)據(jù)
3.SpringBoot中集成
1.導(dǎo)入shiro相關(guān)依賴
<!--
subject 用戶
SecurityManager 管理所有用戶
Realm 連接數(shù)據(jù)
-->
<!--shiro與springboot-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
2.自定義UserRealm
package com.nuist.shirospringboot.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;
/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:59
* @Description
*/
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("執(zhí)行了=》授權(quán)doGetAuthorizationInfo方法");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("執(zhí)行了-》認(rèn)證doGetAuthenticationInfo");
return null;
}
}
3.定義shiroConfig
package com.nuist.shirospringboot.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:58
* @Description
*/
@Configuration
public class ShiroConfig {
// shiroFilterConfiguere
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 設(shè)置用戶管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
// defaultWebSecurity
// 通過@Qualifier 是USerRealm進(jìn)行綁定
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userReaml") UserRealm userReaml) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 關(guān)聯(lián)Realm
securityManager.setRealm(userReaml);
return securityManager;
}
// 創(chuàng)建realm對(duì)象 需要去進(jìn)行自定義,這樣就可以交給spring去進(jìn)行托管了
@Bean
public UserRealm userReaml(){
return new UserRealm();
}
}
4.新建頁面進(jìn)行測(cè)試
package com.nuist.shirospringboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author liuhuanhuan
* @version 1.0
* @date 2022/5/8 17:52
* @Description
*/
@Controller
public class Mycontrollrt
{
@RequestMapping({"/","/index"})
public String toIndex(Model model) {
model.addAttribute("msg","hello shiro");
return "index";
}
@RequestMapping("/user/add")
public String add() {
return "user/add";
}
@RequestMapping("/user/update")
public String update() {
return "user/update";
}
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首頁</h1>
<p th:text="${msg}"></p>
<hr>
<a th:href="@{/user/add}">add </a> | <a th:href="@{/user/update}">update</a>
</body>
</html>
user/add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
user/update.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
此時(shí)一個(gè)簡單的項(xiàng)目就搭建起來了,然后開始shiro的認(rèn)證授權(quán)的操作。
我們只需要在配置中添加如下代碼:
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
filterMap.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setLoginUrl("/toLogin");
此時(shí)就對(duì)我們所有的頁面請(qǐng)求進(jìn)行了攔截,然后轉(zhuǎn)發(fā)到login的頁面
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>login</h1>
<hr>
<form action="">
<p>用戶名:<input type="text" name="username"></p>
<p>密碼:<input type="password" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
此時(shí)在頁面進(jìn)行點(diǎn)擊的時(shí)候,我們就無法正常進(jìn)入頁面,只能進(jìn)入到我們的登錄頁面
進(jìn)行登錄驗(yàn)證的攔截,只有輸入正確的賬號(hào)密碼才能夠進(jìn)入:
MyCOntroller中新增如下代碼:
@RequestMapping("/login")
public String login(String username,String password,Model model) {
// 獲取當(dāng)前用戶
Subject subject = SecurityUtils.getSubject();
// 封裝當(dāng)前用戶
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
try {
subject.login(usernamePasswordToken); // 執(zhí)行登錄的方法,有異常進(jìn)行處理
return "index";
} catch (UnknownAccountException e){
model.addAttribute("msg","用戶名錯(cuò)誤");
return "login";
} catch (IncorrectCredentialsException e) { // 密碼不存在
model.addAttribute("msg","密碼錯(cuò)誤");
return "login";
}
}
然后修改login頁面的代碼
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>login</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
<p>用戶名:<input type="text" name="username"></p>
<p>密碼:<input type="password" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
然后在我們的UserRealm
中doGetAuthenticationInfo方法中新增代碼
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("執(zhí)行了-》認(rèn)證doGetAuthenticationInfo");
String name = "root";
String password = "123456";
UsernamePasswordToken authenticationToken1 = (UsernamePasswordToken) authenticationToken;
if (!authenticationToken1.getUsername().equals(name)) {
return null; // 拋出異常
}
// 密碼認(rèn)證 shiro去做
return new SimpleAuthenticationInfo("",password,"");
}
此時(shí)我們通過UsernamePasswordToken 獲取我們封裝好的賬號(hào)和密碼,但是我們只需要進(jìn)行賬號(hào)的認(rèn)證,密碼的認(rèn)證交給我們的shiro去做就可以了。
具體的用戶授權(quán),我們可以進(jìn)行連接數(shù)據(jù)庫的設(shè)置,但是我為了偷懶,我就不去手動(dòng)創(chuàng)建與數(shù)據(jù)庫的鏈接啦。
下面我們來進(jìn)行頁面的授權(quán)操作
我們想要進(jìn)行用戶的授權(quán)操作
我們需要在shiroConfig中新增如下代碼:
filterMap.put("/user/add","perms[user:add]");
此時(shí)代表如果用戶擁有user:add操作的話,可以顯示,如果沒有的話就不能正常顯示
我們可以自定義一個(gè)頁面來用于返回信息的顯示:
@RequestMapping("/noauth")
@ResponseBody
public String noauth() {
return "未經(jīng)過授權(quán)無法進(jìn)行訪問";
}
當(dāng)用戶沒有add權(quán)限的時(shí)候,我們就提示無法顯示:
此時(shí)我們就完成了單個(gè)的用戶授權(quán)的操作。此時(shí)我們?cè)偃ミM(jìn)行具體的頁面操作
shiro與thymeleaf的結(jié)合:
需要將我們進(jìn)行驗(yàn)證的頁面進(jìn)行如下操作:
<!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</title>
</head>
<body>
<h1>首頁</h1>
<p>
<a th:href="@{/toLogin}">登錄</a>
</p>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add </a>
</div>
|
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
此時(shí)應(yīng)用的方式和springsecurity的方式基本一致。
然后在我們的授權(quán)頁面進(jìn)行操作如下:
UserRealm中修改doGetAuthorizationInfo方法,如下:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("執(zhí)行了=》授權(quán)doGetAuthorizationInfo方法");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 新增授權(quán)頁面
authorizationInfo.addStringPermission("user:add");
// 拿到當(dāng)前對(duì)象 ,然后通過對(duì)象中的授權(quán)方式進(jìn)行判斷
return authorizationInfo;
}
此時(shí)我們賦予用戶只有add的權(quán)限,那么按理說在頁面中是無法顯示update的按鈕,那么我們進(jìn)行測(cè)試下,是否可以正常使用:

此時(shí)認(rèn)證授權(quán)部分已經(jīng)成功啦,以上就是我們進(jìn)行的一個(gè)小小的demo,更深入的學(xué)習(xí),后續(xù)繼續(xù)更新。
到此這篇關(guān)于SpringBoot整合Shiro的方法詳解的文章就介紹到這了,更多相關(guān)SpringBoot整合Shiro內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中使用DOM4J生成xml文件并解析xml文件的操作
這篇文章主要介紹了Java中使用DOM4J來生成xml文件和解析xml文件的操作,今天通過代碼給大家展示了解析xml文件和生成xml文件的方法,需要的朋友可以參考下2021-09-09
SpringBoot+WebSocket搭建簡單的多人聊天系統(tǒng)
WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。這是一種比較官方的說法,簡單點(diǎn)來說就是,在一次TCP連接中,通信的雙方可以相互通信。這篇文章主要介紹了SpringBoot+WebSocket搭建簡單的多人聊天系統(tǒng),需要的朋友可以參考下2019-10-10
mybatis 有時(shí)update語句執(zhí)行無效的解決方案
這篇文章主要介紹了在項(xiàng)目里mybatis有時(shí)update語句執(zhí)行無效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Spring Boot中RabbitMQ自動(dòng)配置的介紹、原理和使用方法
本文介紹了Spring Boot中RabbitMQ自動(dòng)配置的介紹、原理和使用方法,通過本文的介紹,我們希望讀者能夠更好地理解Spring Boot中RabbitMQ的使用方法,并在項(xiàng)目中更加靈活地應(yīng)用,感興趣的朋友跟隨小編一起看看吧2023-07-07
java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法
這篇文章主要為大家詳細(xì)介紹了java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
全解史上最快的JOSN解析庫alibaba Fastjson
這篇文章主要介紹了史上最快的JOSN解析庫alibaba Fastjson,對(duì)FastJson感興趣的同學(xué),一定要看一下2021-04-04

