6.1 整合思路
6.2 配置环境
-
创建项目
-
引入依赖
<!--引入shiro整合Springboot依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.5.3</version> </dependency> <!--引入扩展依赖--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
-
页面中引入命名空间
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> ......
-
常见权限控制标签使用
<!-- 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。 --> <p shiro:guest="">Please <a href="login.html">login</a></p> <!-- 认证通过或已记住的用户。 --> <p shiro:user=""> Welcome back John! Not John? Click <a href="login.html">here</a> to login. </p> <!-- 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 --> <p shiro:authenticated=""> Hello, <span shiro:principal=""></span>, how are you today? </p> <a shiro:authenticated="" href="updateAccount.html">Update your contact information</a> <!-- 输出当前用户信息,通常为登录帐号信息。 --> <p>Hello, <shiro:principal/>, how are you today?</p> <!-- 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 --> <p shiro:notAuthenticated=""> Please <a href="login.html">login</a> in order to update your credit card information. </p> <!-- 验证当前用户是否属于该角色。 --> <a shiro:hasRole="admin" href="admin.html">Administer the system</a><!-- 拥有该角色 --> <!-- 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 --> <p shiro:lacksRole="developer"><!-- 没有该角色 --> Sorry, you are not allowed to developer the system. </p> <!-- 验证当前用户是否属于以下所有角色。 --> <p shiro:hasAllRoles="developer, 2"><!-- 角色与判断 --> You are a developer and a admin. </p> <!-- 验证当前用户是否属于以下任意一个角色。 --> <p shiro:hasAnyRoles="admin, vip, developer,1"><!-- 角色或判断 --> You are a admin, vip, or developer. </p> <!--验证当前用户是否拥有指定权限。 --> <a shiro:hasPermission="userInfo:add" href="createUser.html">添加用户</a><!-- 拥有权限 --> <!-- 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 --> <p shiro:lacksPermission="userInfo:del"><!-- 没有权限 --> Sorry, you are not allowed to delete user accounts. </p> <!-- 验证当前用户是否拥有以下所有角色。 --> <p shiro:hasAllPermissions="userInfo:view, userInfo:add"><!-- 权限与判断 --> You can see or add users. </p> <!-- 验证当前用户是否拥有以下任意一个权限。 --> <p shiro:hasAnyPermissions="userInfo:view, userInfo:del"><!-- 权限或判断 --> You can see or delete users. </p> <a shiro:hasPermission="pp" href="createUser.html">Create a new User</a>
-
加入shrio方言配置
页面标签不起作用一定要记住加入方言处理
@Bean(name = "shiroDialect") public ShiroDialect shiroDialect(){ return new ShiroDialect(); }
6.3 整合配置类
用来整合shiro框架相关的配置类
package com.chif.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.chif.shiro.config.cache.RedisCacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
//1.创建shiroFilter 负责拦截所有请求
//ShiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器 (自动配置)
bean.setSecurityManager(defaultWebSecurityManager);
//配置系统受限资源
/*
anon:无需认证就可以访问
outhc:必须认证了才能访问
user:必须拥有记住我功能才能使用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//授权,正常的情况下,没有授权会跳转到未授权的页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/*","authc");//authc 请求这个资源需要认证和授权
// filterMap.put("/*","authc");//authc 请求这个资源需要认证和授权
filterMap.put("/login","anon");//anon 设置为公共资源
filterMap.put("/getImage","anon");//验证码 设置为公共资源
filterMap.put("/register","anon");//anon 设置为公共资源
//添加shiro的内置过滤器
bean.setFilterChainDefinitionMap(filterMap);
//配置系统公共资源
//设置默认认证界面 //默认认证界面路径就是 login.xxx
bean.setLoginUrl("/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noauth");
return bean;
}
//2.创建安全管理器
//DefaultWebSecurityManager 2
@Bean(name = "SecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//给安全管理器关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建 realm 对象 ,需要自定义类 1
@Bean(name = "userRealm")
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
//修改凭证校验匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法为md5
hashedCredentialsMatcher.setHashAlgorithmName("Md5");
//设置散列的次数
hashedCredentialsMatcher.setHashIterations(1024);
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
//开启缓存管理
userRealm.setCacheManager(new RedisCacheManager());
userRealm.setCachingEnabled(true);//开启全局缓存
userRealm.setAuthenticationCachingEnabled(true);//开启认证缓存
userRealm.setAuthenticationCacheName("authenticationCache");
userRealm.setAuthorizationCachingEnabled(true);//开启授权缓存
userRealm.setAuthorizationCacheName("authorizationCache");
return userRealm;
}
}
6.4 常见过滤器
注意: shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置控制指定url的权限:
6.5 自定义Realm
package com.chif.shiro.config;
import com.chif.shiro.config.salt.MyByteSource;
import com.chif.shiro.pojo.Perms;
import com.chif.shiro.pojo.User;
import com.chif.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
//自定义的 USerRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取身份信息
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println("调用授权验证"+primaryPrincipal);
//根据主身份信息获取角色 和 权限信息
User user = userService.findRolesByUsername(primaryPrincipal);
//授权角色信息
if (!CollectionUtils.isEmpty(user.getRoles())){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
user.getRoles().forEach(role -> {
simpleAuthorizationInfo.addRole(role.getName());
//授予权限信息
List<Perms> perms = userService.findPermsByRoleId(role.getId());
if (!CollectionUtils.isEmpty(perms) && perms.get(0)!=null){
/**
* 注意:如果你创建了一个用户,并为这个用户授予了一个角色,但这个角色并未关联任何的 授权字符串,
* 那么调用数据库获得的结果是 List<Perms> perms=[null],
* 此时 perms已经被初始化,里面只有一个属性null,使用判空的方法无法判别,
* 此时继续遍历会报出空指针异常,此时应当添加判断条件 perms.get(0)!=null
*/
perms.forEach(perm -> {
simpleAuthorizationInfo.addStringPermission(perm.getName());
});
}
});
return simpleAuthorizationInfo;
}
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
//用户名 密码 数据库中取
//String name="root";
//String password="123456";
//用户token
String principal= (String) token.getPrincipal();
//连接真实的数据库
User user = userService.queryUserByName(principal);
System.out.println(user);
if (user==null){//没有这个人
return null;//抛出异常 未知用户名
}
//参数1:返回数据库中正确的用户名
//参数2:返回数据库中正确密码
//参数3:注册时的随机盐
//参数4:提供当前realm的名字
return new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
new MyByteSource(user.getSalt()),
this.getName());
}
}
6.6 认证和退出的实现
Controller层实现
@RequestMapping("/login")
public String login(String username,String password,String code,Model model,HttpSession session){
//比较验证码
String codes = (String) session.getAttribute("code");
try {
if (codes.equalsIgnoreCase(code)){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);//执行登陆方法,如果没有异常就说明OK
return "index";
}else {
throw new RuntimeException("验证码错误");
}
}catch (UnknownAccountException e){//用户不存在
model.addAttribute("msg","用户名不存在");
return "login";
}catch (IncorrectCredentialsException e){//密码错误
model.addAttribute("msg","密码错误");
return "login";
}catch (Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
}
return "login";
}
@RequestMapping("/noauth")
public String unauthorized(){
return "未经授权无法访问次页面";
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();//退出用户
return "login";
}
生成salt的工具类
package com.chif.shiro.utils;
import java.util.Random;
public class SaltUtils {
/**
* 生成salt的静态方法
* @param n
* @return
*/
public static String getSalt(int n){
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
char aChar = chars[new Random().nextInt(chars.length)];
sb.append(aChar);
}
return sb.toString();
}
public static void main(String[] args) {
String salt=getSalt(4);
System.out.println(salt);
}
}
6.7 连接mtbatis数据库
-
设计数据库表
-
创建数据库表
/* Navicat Premium Data Transfer Source Server : localhost_3306 Source Server Type : MySQL Source Server Version : 80015 Source Host : localhost:3306 Source Schema : db_history Target Server Type : MySQL Target Server Version : 80015 File Encoding : 65001 Date: 25/07/2021 09:17:33 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_perms -- ---------------------------- DROP TABLE IF EXISTS `t_perms`; CREATE TABLE `t_perms` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for t_role_perms -- ---------------------------- DROP TABLE IF EXISTS `t_role_perms`; CREATE TABLE `t_role_perms` ( `id` int(11) NOT NULL AUTO_INCREMENT, `roleid` int(11) NULL DEFAULT NULL, `permsid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userid` int(11) NULL DEFAULT NULL, `roleid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `perms` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
-
创建实体类
User
@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private String id; private String username; private String password; private String salt; //定义角色集合 private List<Role> roles; }
Role
@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor public class Role implements Serializable { private String id; private String name; //定义权限的集合 private List<Perms> perms; }
Perms
@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor public class Perms implements Serializable { private String id; private String name; private String url; }
-
创建dao方法
//根据用户名查询所有角色 User findRolesByUserName(String username); //根据角色id查询权限集合 List<Perms> findPermsByRoleId(String id);
-
mapper实现
<resultMap id="userMap" type="User"> <id column="uid" property="id"/> <result column="username" property="username"/> <!--角色信息--> <collection property="roles" javaType="list" ofType="Role"> <id column="id" property="id"/> <result column="rname" property="name"/> </collection> </resultMap> <select id="findRolesByUserName" parameterType="String" resultMap="userMap"> SELECT u.id uid,u.username,r.id,r.NAME rname FROM t_user u LEFT JOIN t_user_role ur ON u.id=ur.userid LEFT JOIN t_role r ON ur.roleid=r.id WHERE u.username=#{username} </select> <select id="findPermsByRoleId" parameterType="String" resultType="Perms"> SELECT p.id,p.NAME,p.url,r.NAME FROM t_role r LEFT JOIN t_role_perms rp ON r.id=rp.roleid LEFT JOIN t_perms p ON rp.permsid=p.id WHERE r.id=#{id} </select>
service等等…
-
数据库添加(
权限角色
)信息/* Navicat Premium Data Transfer Source Server : localhost_3306 Source Server Type : MySQL Source Server Version : 80015 Source Host : localhost:3306 Source Schema : db_history Target Server Type : MySQL Target Server Version : 80015 File Encoding : 65001 Date: 25/07/2021 09:27:58 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_perms -- ---------------------------- DROP TABLE IF EXISTS `t_perms`; CREATE TABLE `t_perms` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_perms -- ---------------------------- INSERT INTO `t_perms` VALUES (1, 'user:*:*', NULL); INSERT INTO `t_perms` VALUES (2, 'user:add:02', NULL); INSERT INTO `t_perms` VALUES (3, 'user:update:*', NULL); -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_role -- ---------------------------- INSERT INTO `t_role` VALUES (1, 'admin'); INSERT INTO `t_role` VALUES (2, 'user'); INSERT INTO `t_role` VALUES (3, 'product'); -- ---------------------------- -- Table structure for t_role_perms -- ---------------------------- DROP TABLE IF EXISTS `t_role_perms`; CREATE TABLE `t_role_perms` ( `id` int(11) NOT NULL AUTO_INCREMENT, `roleid` int(11) NULL DEFAULT NULL, `permsid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_role_perms -- ---------------------------- INSERT INTO `t_role_perms` VALUES (1, 1, 1); INSERT INTO `t_role_perms` VALUES (2, 1, 2); INSERT INTO `t_role_perms` VALUES (3, 2, 3); -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userid` int(11) NULL DEFAULT NULL, `roleid` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_user_role -- ---------------------------- INSERT INTO `t_user_role` VALUES (1, 1, 1); INSERT INTO `t_user_role` VALUES (2, 2, 2); INSERT INTO `t_user_role` VALUES (3, 3, 3); -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `perms` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'admin', '18010d23f6bd4d702e35c849a6790af9', 'user:*:01', 'b%pLB!*q'); INSERT INTO `user` VALUES (2, 'chif', 'd4feed7e70ce0269f5d85135a34f0d6d', 'user:*:02', 'HpR*B(pk'); INSERT INTO `user` VALUES (5, 'liu', 'd4feed7e70ce0269f5d85135a34f0d6d', 'user:*:05', 'HpR*B(pk'); INSERT INTO `user` VALUES (16, 'qq', '18010d23f6bd4d702e35c849a6790af9', NULL, 'b%pLB!*q'); INSERT INTO `user` VALUES (17, 'ww', 'd4feed7e70ce0269f5d85135a34f0d6d', NULL, 'HpR*B(pk'); SET FOREIGN_KEY_CHECKS = 1;
6.8 使用缓存Cachemanager
6.8.1 Cache 作用
- Cache 缓存: 计算机内存中一段数据
- 作用: 用来减轻DB的访问压力,从而提高系统的查询效率
- 流程:
6.8.2 使用shiro中默认EhCache实现缓存
1.引入依赖
<!--引入shiro和ehcache-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
2.开启缓存
//3.创建自定义realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//修改凭证校验匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法为md5
credentialsMatcher.setHashAlgorithmName("MD5");
//设置散列次数
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
//开启缓存管理器
customerRealm.setCachingEnabled(true);
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setCacheManager(new EhCacheManager());
return customerRealm;
}
3.启动刷新页面进行测试
-
注意:如果控制台没有任何sql展示说明缓存已经开启
# 开启控制台SQL日志注解 logging.level.com.chif.shiro.mapper=debug