(视频:哔哩哔哩-编程不良人)
一:权限的管理(1~3)
1. 什么是权限管理:
3. 什么是身份认证:
5. 什么是授权:
二:什么是 Shiro(4~4)
1. 官网:https://shiro.apache.org/
2. 英文文档:https://shiro.apache.org/reference.html
3. 中文文档:http://greycode.github.io/shiro/doc/reference.html
三:Shiro 的核心架构(4~4)
1. Shiro 核心架构图:(视频p4:详解)
3. 模块解释说明:
四:Shiro中的认证(5~5)
1. 认证:
2. Shiro 中认证的关键对象:
3. 认证大体流程:
4. 认证的开发:
1)引入 shiro 依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.8.0</version>
</dependency>
’ 2)创建:shiro.ini 文件
[users]
zhangsan=123456
lisi=123456
wangwu=123456
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
’ 3)完成认证:
public static void main(String[] args) {
//1:创建安全管理器
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2:给安全管理器设置 Realm
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
//3:给全局工具类,设置全局安全管理器
SecurityUtils.setSecurityManager(securityManager);
//4:获取主体对象
Subject subject = SecurityUtils.getSubject();
//5:创建安全令牌
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("zhangsan", "123456");
usernamePasswordToken.setRememberMe(true);
try {
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
System.out.println("账户错误:" + e);
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误:" + e);
}
boolean authenticated = subject.isAuthenticated();
System.out.println("用户是否登陆:" + authenticated);
subject.logout();
boolean authenticated2 = subject.isAuthenticated();
System.out.println("用户是否登陆:" + authenticated2);
}
5. 认证流程源码查看:
0)最终执行用户比较:SimpleAccountRealm。doGetAuthenticationInfo() 方法中完成用户名校验
0)最终密码校验是在 AuthenticatingRealm 中,assertCredentialsMatch()。
1)完成用户名校验:(SimpleAccountRealm)(方法中实现,可换成自己去读取数据库)
2)完成密码校验:(AuthenticatingRealm)
3)Realm 继承关系图:(自定义 Realm 继承 AuthorizingRealm 类)
4)以 SimpleAccountRealm 为例:
5)总结:
- 1.AuthenticatingRealm --> 认证Realm–>doGetAuthenticationInfo()
- 2.AuthorizingRealm --> 授权Realm --> getAuthorizationInfo()
- 3.如果需要自定义 Realm,参考 SimpleAccountRealm 继承 AuthorizingRealm,才可覆盖认证、授权,两个方法。
6. 自定义 Realm 实现:( securityManager.setRealm(new MyRealm()); )
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private BlogUserService blogUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
System.out.println("username:" + principal);
//根据用户名查询数据库
BlogUserEntity blogUserDB = blogUserService.getBlogUserByUsername(principal);
if (!ObjectUtils.isEmpty(blogUserDB)) {
//用户名、盐值、密码、//当前realm名称
return new SimpleAuthenticationInfo(blogUserDB.getUsername(), "", blogUserDB.getPassword());
// return new SimpleAuthenticationInfo(blogUserDB.getUsername(), blogUserDB.getPassword(), this.getName());
}
return null;
}
}
public static void main(String[] args) {
SpringApplication.run(MyShiroDemo01Application.class, args);
final Md5Hash md5Hash = new Md5Hash("123", "salt",1024);
String resultStr = md5Hash.toHex();
System.out.println(resultStr);
// 1:创建 securityManager
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2:设置自定义 Realm
securityManager.setRealm(new MyRealm());
// 3:通过安全工具类,设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4:通过安全工具类,获取 subject 主体
Subject subject = SecurityUtils.getSubject();
// 5:创建 token
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("张三", "123");
usernamePasswordToken.setRememberMe(true);
// 6:登陆操作
try {
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
System.out.println("用户名出错");
} catch (IncorrectCredentialsException e2) {
System.out.println("密码出错");
}
System.out.println(subject.isAuthenticated());
}
五:MD5 和 Salt 的简介和执行流程说明(6~9)
1. MD5 算法简介:
1)作用:一般用来加密、签名。
2)特点:MD5 算法不可逆,内容相同,无论执行多少次,结果始终一样。
3)结果:始终是一个 16进制,32位长度的字符串。
2. 使用:
// 1:加盐
final Md5Hash md5Hash = new Md5Hash("123", "salt");
String resultStr = md5Hash.toHex();
System.out.println(resultStr);
// 2:加盐加散列
final Md5Hash md5Hash = new Md5Hash("123", "salt", 1024);
String resultStr = md5Hash.toHex();
System.out.println(resultStr);
3. 实现:(使用自定义 Realm,加入:md5、salt、hash散列)
’ 0)凭证匹配器继承关系:在自定义 Realm 中,进行设定凭证匹配器。
’ 1)md5:
public static void main(String[] args) {
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 1、设置 Realm 为 Hash 凭证匹配器
MyMd5Realm myMd5Realm = new MyMd5Realm();
// 2.1、自定义 Realm 设置,自定义凭证匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 2.2、加密算法、散列次数
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1024);
// 2.3、给自定义 Realm,设置自定义凭证匹配器
myMd5Realm.setCredentialsMatcher(matcher);
// 3:给安全管理器设置 Realm
securityManager.setRealm(myMd5Realm);
// 4:给全局工具类,设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
// 5、创建 token
UsernamePasswordToken token = new UsernamePasswordToken("张三", "123456");
token.setRememberMe(true);
try {
subject.login(token);
System.out.println(subject.isAuthenticated());
} catch (UnknownAccountException e) {
System.out.println("用户名错误");
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误");
}
}
public class MyMd5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
System.out.println(principal);
if ("张三".equals(principal)) {
return new SimpleAuthenticationInfo(
"张三",
"e10adc3949ba59abbe56e057f20f883e",
ByteSource.Util.bytes("sss"),
this.getName());
}
return null;
}
}
’ 2)md5 + 加随机盐值:
/**
* 参数:数据库用户名、数据库加密后的密码,
* 注册时的随机盐值、realm的名字;
*
* 只需要加一次盐值,shiro会自动把用户输入密码加盐值。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
System.out.println(principal);
if ("张三".equals(principal)) {
// 用户名、密码,都为数据库中存储的加密后的
return new SimpleAuthenticationInfo("张三",
"5194528bd63abca4bf9d09885bb425b7",
ByteSource.Util.bytes("sss"),
this.getName());
}
return null;
}
‘ 3)md5 + 加随机盐值::(1024次)(除了上面加盐的地方,Realm声明散列次数)
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//设置 Realm 为 Hash 凭证匹配器
MyMd5Realm myMd5Realm = new MyMd5Realm();
//设置hash算法
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1024);
// assertCredentialsMatch()方法,会获取默认凭证匹配器,
// 所以,要先设置自定义凭证匹配器,放入hash算法规则
myMd5Realm.setCredentialsMatcher(matcher);
//设置Realm
securityManager.setRealm(myMd5Realm);
SecurityUtils.setSecurityManager(securityManager);
六:授权(10~11)
1. 授权:
3. 关键对象:
5. 授权流程:
7. 授权方式:
9. 权限字符串详解:
10. shiro 中授权编码实现方式
’ 1)编程式:
’ 2)注解式:
’ 3)标签式:
12. 角色管理:实际运用:
’ 1)验证完身份,给主体授权操作:
public class MyMd5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//进行给当前主体授权
HashSet<String> hashSet = new HashSet<>();
hashSet.add("admin");
hashSet.add("user");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(hashSet);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
’ 2)验证主体是否有某种角色:(3种方法)
//验证身份通过后,进行授权
if (subject.isAuthenticated()) {
//1.判断主体是否具有某个权限
System.out.println(subject.hasRole("admin"));
System.out.println(subject.hasRole("user"));
//2.判断主体是否同时满足这两个条件
System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));
//3.判断主体是否具有某个权限
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user"));
for (boolean b : booleans) {
System.out.println(b);
}
}
14.(权限)基于权限字符串的访问控制:实际操作:
’ 1):(资源标识符:操作:资源类型)
public class MyMd5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
//可根据用户名,获取数据库的 角色信息、权限信息。
//将数据库查询到 角色 信息,赋值给权限对象
HashSet<String> hashSet = new HashSet<>();
hashSet.add("admin");
hashSet.add("user");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(hashSet);
//将数据库查询到 权限 信息,赋值给权限对象
simpleAuthorizationInfo.addStringPermission("user:*:001");
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
’ 2)判断是否有权限:(权限验证)
//验证身份通过后,进行验证
if (subject.isAuthenticated()) {
//1.基于权限字符串访问控制
System.out.println(subject.isPermitted("user:*:*"));//false
System.out.println(subject.isPermitted("user:*:001"));//true
System.out.println(subject.isPermitted("user:update:001"));//true
//2.同时具有哪些权限
boolean permittedAll = subject.isPermittedAll("user:*:002", "user:*:001");
System.out.println(permittedAll);//false
//3.分别具有哪些权限
boolean[] permitted = subject.isPermitted("user:*:002", "user:*:001");
System.out.println(Arrays.toString(permitted));//[false, true]
}
七:SpringBoot 整合 shiro 之 环境搭建(12~14)
1. 认证授权流程:
2. 创建springboot应用:
’ 1)整合 jsp 依赖:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
’ 2)yml 配置:
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://114.215.173.88:3306/shiro-test-01
username: root
password: 123456
mvc:
view:
prefix: /
suffix: .jsp
mybatis:
type-aliases-package: com.shiro.myshirodemo02jsp.entity
mapper-locations: classpath:mapper/*.xml
’ 3)访问:http://localhost:8080/index.jsp
如果启动失败,需设置工作目录:
3. 配置 Shiro 相关:
’ 1)引入 springboot 整合 shiro 依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
’ 2)ShiroConfig:(3个 Bean 配置)
@Configuration
public class ShiroConfig {
// 1:创建 shiro filter,//负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 给 filter 设置 安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置拦截路径
HashMap<String, String> filterMap = new HashMap<>();
filterMap.put("/index.jsp", "authc");//authc:标识请求此资源需要认证和授权
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 设置默认的认证界面路径
shiroFilterFactoryBean.setLoginUrl("/login.jsp");
return shiroFilterFactoryBean;
}
// 2:创建 安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 3:创建 realm
@Bean
public Realm getRealm() {
return new CustomerRealm();
}
}
’ 3)MyRealm:
//自定义Realm
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 后面补充
return null;
}
}
’ 4)请求测试:(注:如使用 thymleaf,需要控制器跳转到页面)
访问:http://localhost:8080/index.jsp,跳转到登陆页面
八:整合 Shiro 之 认证和退出(15~15)
1. shiro 提供的 常见 过滤器:
2. 写登陆页面:
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>登陆页面</title>
</head>
<body>
<h1>我的登陆页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
<input type="submit" value="登陆">
</form>
</body>
</html>
’ 4)controller:
@Controller
@RequestMapping(value = "/user")
public class ShiroController {
@PostMapping(value = "/login")
public String login(String username, String password) {
System.out.println(username + ":" + password);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
try {
subject.login(usernamePasswordToken);
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
System.out.println("账号错误");
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误");
}
return "redirect:/login.jsp";
}
}
’ 5)编写认证管理器:
//自定义Realm
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
BlogUserEntity blogUserDB = shiroUserService.getUserByUsername(username);
if (blogUserDB != null) {
return new SimpleAuthenticationInfo(
blogUserDB.getUsername(),
blogUserDB.getPassword(),
this.getName());
}
return null;
}
}
’ 6)退出登陆:
1.index.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>菜单页面</title>
</head>
<body>
<h1>菜单页面</h1>
<ul>
<li><a href="">用户管理</a></li>
<li><a href="">订单管理</a></li>
<li><a href="">商品管理</a></li>
<li><a href="">物流管理</a></li>
</ul>
<h1><a href="${pageContext.request.contextPath}/user/logout">退出登陆</a></h1>
</body>
’ 2.controller:
@GetMapping(value = "/logout")
public String logout() {
// 通过安全工具类,获取主体信息并登出
Subject subject = SecurityUtils.getSubject();
if (subject != null && subject.isAuthenticated()) {
subject.logout();
}
return "redirect:/login.jsp";
}
九:认证 + salt + 散列(16~17)
1. 创建数据库:
CREATE TABLE `blog_user` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
`salt` varchar(255) NOT NULL DEFAULT '' COMMENT '盐值',
PRIMARY KEY (`id`)
);
3. mybatis 整合:
’ 1)相关依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
’ 2)相关配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://114.215.173.88:3306/testdb
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
type-aliases-package: com.shiro.myshirodemo02jsp.entity
mapper-locations: classpath:mybatis/mapper/*.xml
’ 3)mapper.xml
<?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.shiro.myshirodemo02jsp.dao.ShiroUserDao">
<insert id="addUser">
insert into blog_user (username, password, salt)
values (#{username}, #{password}, #{salt})
</insert>
<select id="getUserByUsername" resultType="com.example.myshirodemo02testjsp.entity.BlogUserEntity">
select *
from blog_user
where username = #{principal}
</select>
</mapper>
5. 用户注册:
’ 1)jsp:
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>登陆页面</title>
</head>
<body>
<h1>我的登陆页面</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
<input type="text" name="username"><br>
<input type="text" name="password"><br>
<input type="submit" value="用户注册"><br>
</form>
</body>
</html>
’ 2)controller:
@Controller
@RequestMapping(value = "/user")
public class ShiroController {
@Autowired
private ShiroUserService shiroUserService;
@PostMapping(value = "/register")
public String register(String username, String password) {
System.out.println(username + ":" + password);
Md5Hash newPassword = new Md5Hash(password, "salt", 1024);
shiroUserService.addUser(username, newPassword.toHex(), "salt");
return "redirect:/user/login.jsp";
}
}
7. 用户登陆:(md5 + 随机盐 认证)
’ 1)修改 Realm 认证方式:
@Configuration
public class ShiroConfig {
@Bean
public Realm getRealm() {
MyRealm myRealm = new MyRealm();
//修改凭证校验匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
}
’ 2)修改 :
@Autowired
private ShiroUserService shiroUserService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
BlogUserEntity blogUserDB = shiroUserService.getUserByUsername(username);
if (blogUserDB != null) {
return new SimpleAuthenticationInfo(
blogUserDB.getUsername(),
blogUserDB.getPassword(),
ByteSource.Util.bytes("salt"),
this.getName());
}
return null;
}
’ 3)customerRealm 中,无法获取容器中 service 实例,解决方案。
i:增加工具类,用于获取容器中对象实例:
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
public static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
/**
* 根据 bean 名字,获取工厂中指定的 bean 对象
*/
public static Object getBean(String beanName) {
return context.getBean(beanName);
}
}
ii:修改 注入 service 方法:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
// 获取容器中对象实例
ShiroUserService shiroUserService = (ShiroUserService) ApplicationContextUtils.getBean("shiroUserServiceImpl");
BlogUserEntity blogUserDB = shiroUserService.getUserByUsername(username);
if (blogUserDB != null) {
return new SimpleAuthenticationInfo(
blogUserDB.getUsername(),
blogUserDB.getPassword(),
ByteSource.Util.bytes("salt"),
this.getName());
}
return null;
}
十:整合 shiro 之授权基本使用:(18~20)
1. 授权管理器编码:(基于角色权限管理)
@Component
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
//根据主体,获取角色信息,权限信息
if ("zhangsan".equals(primaryPrincipal)) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
return simpleAuthorizationInfo;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
3. index.jsp 页面控制:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>菜单页面</title>
</head>
<body>
<h1>菜单页面</h1>
<ul>
<shiro:hasAnyRoles name="admin,user">
admin、user角色可以看到
<li><a href="">用户管理</a></li>
</shiro:hasAnyRoles>
<shiro:hasRole name="user">
admin 角色可以看到
<li><a href="">订单管理</a></li>
<li><a href="">商品管理</a></li>
<li><a href="">物流管理</a></li>
</shiro:hasRole>
<li><a href="${pageContext.request.contextPath}/user/logout">退出登陆</a></li>
</ul>
</body>
</html>
5. 效果:
7. 基于权限字符串做权限管理:
’ 1)授权管理编码:
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private ShiroUserService shiroUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
//根据主体,获取角色信息,权限信息
if ("zhangsan".equals(primaryPrincipal)) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
//设置权限字符串
simpleAuthorizationInfo.addStringPermission("user:*:*");
return simpleAuthorizationInfo;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
’ 2)index.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>菜单页面</title>
</head>
<body>
<h1>菜单页面</h1>
<ul>
<shiro:hasAnyRoles name="admin,user">
admin、user角色可以看到
<li>
<a href="">用户管理</a>
<shiro:hasPermission name="user:add:*">
<ul><a href="">增加用户</a></ul>
</shiro:hasPermission>
<shiro:hasPermission name="user:delete:*">
<ul><a href="">删除用户</a></ul>
</shiro:hasPermission>
<shiro:hasPermission name="user:update:*">
<ul><a href="">修改用户</a></ul>
</shiro:hasPermission>
<shiro:hasPermission name="user:find:*">
<ul><a href="">查询用户</a></ul>
</shiro:hasPermission>
</li>
</shiro:hasAnyRoles>
<li><a href="${pageContext.request.contextPath}/user/logout">退出登陆</a></li>
</ul>
</body>
</html>
’ 3)测试效果:
9. controller 两种实现:
’ 1)编码式:
@RestController
@RequestMapping(value = "/user")
public class ShiroUserController {
@RequestMapping(value = "/addUser")
public String add() {
Subject subject = SecurityUtils.getSubject();
if (subject.hasRole("admin")) {
System.out.println("新增成功");
} else {
System.out.println("没有权限");
}
return null;
}
}
’ 2)注解式:
@RequestMapping(value = "/addUser2")
@RequiresRoles(value = {"admin"})
public String add2() {
System.out.println("新增成功");
return null;
}
’ 3)权限字符串形式
@RequestMapping(value = "/addUser2")
// @RequiresRoles(value = {"admin"})
@RequiresPermissions(value = {"user:add:*"})
public String add2() {
System.out.println("新增成功");
return null;
}
十一:整合 shiro 连接数据库(16~17)
1. 数据库、表结构设计
2. 创建数据库:
-- 用户和角色的关系
CREATE TABLE `user_role` (
`id` bigint(20) unsigned PRIMARY KEY AUTO_INCREMENT,
`role_id` bigint(20),
`user_id` bigint(20)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 角色表
CREATE TABLE `role` (
`id` bigint(20) unsigned PRIMARY KEY AUTO_INCREMENT,
`name` varchar(128) default "" COMMENT '名称'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 角色与权限的关系表
CREATE TABLE `role_permission` (
`id` bigint(20) unsigned PRIMARY KEY AUTO_INCREMENT,
`role_id` bigint(20),
`permission_id` bigint(20)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 权限表
CREATE TABLE `permission` (
`id` bigint(20) unsigned PRIMARY KEY AUTO_INCREMENT,
`name` varchar(128) default "" COMMENT '名称',
`url` varchar(128) default "" COMMENT '接口路径'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 构建用户、角色、权限 实体类:
4. dao 层查询方法:
’ 1)根据用户名查询所有角色:
<select id="findByUsername" resultType="com.shiro.myshirodemo02jsp.entity.RoleEntity">
SELECT r.name
from shiro_user su
left join user_role ur on
su.id = ur.user_id
left JOIN `role` r on
ur.role_id = r.id
where su.username = #{username}
</select>
’ 2)业务层实现:
public class MyRealm extends AuthorizingRealm {
@Autowired
private ShiroUserService shiroUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
//根据主体,获取角色信息,权限信息
List<String> allRoleByUsername = shiroUserService.findAllRoleByUsername(primaryPrincipal);
if (allRoleByUsername != null) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(allRoleByUsername);
return simpleAuthorizationInfo;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
6. 查询用户对应的权限字符串,并赋值:
’ 1)根据用户查询角色、根据角色查询对应的权限字符串。
’ 2)在 自定义 Realm 中赋值。
十二:SpringBoot 默认 缓存之 EhCache(21~21)
1. Cache 作用(下)
2. 引入 shiro 与 ehcache 依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
3. 在自定义 Realm中,开启缓存管理器
// 3:创建 realm
@Bean(value = "realm")
public Realm realm() {
CustomerRealm customerRealm = new CustomerRealm();
// 修改自定义凭证匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(matcher);
// 开启缓存管理器
customerRealm.setCacheManager(new EhCacheManager());
customerRealm.setCachingEnabled(true);
// 设置认证缓存
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("authenticationCacheName");
// 设置授权缓存
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("authorizationCacheName");
return customerRealm;
}
十三:SpringBoot 缓存之 Redis:(22~23)
1. 使用 CacheManager;Cache 作用:
2. 引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3. yml 配置:
spring:
redis:
host: 114.215.173.88
port: 6379
4. 配置 redis 缓存管理器:
’ 1)开启缓存:
@Configuration
public class ShiroConfig {
// 3:创建 realm
@Bean(value = "realm")
public Realm realm(RedisCacheManager redisCacheManager) {
CustomerRealm customerRealm = new CustomerRealm();
// 修改自定义凭证匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(matcher);
customerRealm.setCacheManager(new RedisCacheManager());
// 开启全局缓存管理器
customerRealm.setCachingEnabled(true);
// 设置认证缓存
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthenticationCacheName("authenticationCacheName");
// 设置授权缓存
customerRealm.setAuthorizationCachingEnabled(true);
customerRealm.setAuthorizationCacheName("authorizationCacheName");
return customerRealm;
}
}
‘ 2)自定义 shiro 缓存管理器:
@Component
public class RedisCacheManager implements CacheManager {
@Autowired
private RedisCache redisCache;
@Override
public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
System.out.println("cacheName ===" + cacheName);
return redisCache;
}
}
认证数据加入缓存,user要序列化
盐不能序列化,需处理盐
补其他方法:
2、
3、
4、
- 1
Shiro 整合 thymeleaf
1. 引入 拓展依赖:
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2. 页面中 引入命名空间:
3. 配置文件:
4.
- 2
- 2
- 2
- 2