Shiro学习笔记(二)Shiro实现用户授权及简单整合springboot
授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限时无法访问的。
授权流程
- 系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager。
- SecurityManager将权限检测操作委托给Authorizer对象。
- Authorizer将用户信息委托给realm。
- Realm访问数据库获取用户权限信息并封装。
- Authorizer对用户授权信息进行判定。
授权方式
-
基于角色的访问控制
-
RBAC基于角色的访问能控制(Role-Based Access Control)是以角色为中心进行访问控制
-
if(Subject.hasRole("admin")){ //操作什么资源 }
-
-
基于资源的访问控制
-
RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制。
-
if(subject.isPermission("user:update:01")){ //对01用户进行修改 } if(subject.isPermission("user:update:*")){ //可以对用户进行修改 }
-
权限字符串
权限字符串规则:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作,“:”是资源、操作、实例的分隔符,权限字符串也可以使用*通配符。
例子:
- 用户创建权限:user:create,或user:create:*
- 用户修改实例001的权限:user:update:001
- 用户实例001的所有权限:user:*:001
Shiro中授权的实现方式
-
编程式
-
Subject subject = SecurityUtils.getSubject(); if(subject.hasRole("adming")){ //有权限 }else{ //没有权限 }
-
-
注解式
-
@RequiresRoles("admin") public void hello(){ }
-
-
标签式
-
JSP/GSP 标签:在JSP/GSP页面进行相应的标签完成: <shiro:hasRole name="admin"> <!- 有权限 -> </shiro:hasRole> 注意:Thymeleaf 中使用shiro需要额外集成
-
开发授权
realm实现
package com.lany.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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;
/**
* @Description 使用自定义realm 加入md5+salt+hash
* @Author 十里
* @Date 2021/4/22 21:01
*/
public class CustomerMd5Realm extends AuthorizingRealm {
/**
* @Description 授权
* @Author 十里
* @Date 2021/4/22 20:59
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
System.out.println("身份信息:" + primaryPrincipal);
//根据身份信息 用户名 获取当前用户的角色信息,以及权限信息 zhangsan admin user
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//将数据库中查询角色信息复制给权限对象
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
//将数据库中查询权限信息赋值给权限对象
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create");
return simpleAuthorizationInfo;
}
/**
* @Description 认证
* @Author 十里
* @Date 2021/4/22 20:59
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取身份信息
String principal = (String) token.getPrincipal();
//根据用户名查询数据库
if ("zhangsan".equals(principal)) {
//参数1:数据库用户名,参数2:数据库md5+Salt之后的密码 参数3:注册时的随机盐 参数4:realm的名字
return new SimpleAuthenticationInfo(principal,
"0e2cc6db0f430296bac325822f99bb62",
ByteSource.Util.bytes("X0*ps"),
this.getName());
}
return null;
}
}
授权
package com.lany;
import com.lany.realm.CustomerMd5Realm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import java.util.Arrays;
/**
* @author 十里
* @date 2021年04月22日 21:02
*/
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//注入realm
CustomerMd5Realm realm = new CustomerMd5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(credentialsMatcher);
defaultSecurityManager.setRealm(realm);
//将安全管理器注入安全工具
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
subject.login(token);
System.out.println("登录成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}
//认证用户进行授权
if (subject.isAuthenticated()) {
//1.基于角色权限控制
System.out.println(subject.hasRole("user"));
System.out.println("===============");
//基于多角色权限认证
System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));
System.out.println("===============");
//是否具有其中一个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
for (boolean aBoolean : booleans) {
System.out.println(aBoolean);
}
System.out.println("===============");
//基于权限字符串的访问控制 资源标识符:操作:资源类型
System.out.println("权限:" + subject.isPermitted("user:update:01"));
System.out.println("权限:" + subject.isPermitted("product:create"));
System.out.println("===============");
//分别具有那些权限
boolean[] permitted = subject.isPermitted("user:*:01", "product:create");
for (boolean b : permitted) {
System.out.println(b);
}
System.out.println("===============");
//同时具有那些权限
System.out.println(subject.isPermittedAll("user:*:01", "product:create"));
}
}
}
整合SpringBoot项目实战
整合思路
-
创建springboot项目
-
引入依赖
-
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.5.3</version> </dependency>
-
-
配置shiro环境
-
创建配置类
-
@Configuration public class ShiroConfig{ //创建ShiroFilter @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){} //创建web管理器 @Bean public DefaultWebSecurityManager getDefaultSecurityManager(Realm realm){} //创建自定义realm @Bean public Realm getRealm(){} }
-
-
配置ShiroFilterFactoryBean
-
/** * 1.创建shiroFIlter 负责拦截所有请求 * * @return */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //给filter设置安全管理器 shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); //配置系统首先资源 //配置系统公共资源 HashMap<String, String> map = new HashMap<>(); map.put("/user/login", "anon");//anon 设置为公共资源 map.put("/**", "authc");// authc 请求这个资源需要认证和授权 shiroFilterFactoryBean.setFilterChainDefinitionMap(map); //默认认证界面路径 shiroFilterFactoryBean.setLoginUrl("/login.jsp"); return shiroFilterFactoryBean; }
-
-
配置WebSecurityManager
-
/** * 2.创建安全管理器 * * @return */ @Bean public DefaultWebSecurityManager getDefaultSecurityManager(Realm realm) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); //给安全管理器设置realm defaultWebSecurityManager.setRealm(realm); return defaultWebSecurityManager; }
-
这里配置的是DefaultWebSecurityManager,而不是DefaultSecurityManager,因为现在是web开发,所以需要使用DefaultWebSecurityManager,如果使用错安全管理器,会报错。
-
-
创建自定义Realm
-
/** * 3.创建自定义Realm * * @return */ @Bean public Realm getRealm() { CustomerRealm customerRealm = new CustomerRealm(); return customerRealm; }
-
package com.lany.springboot_jsp_shiro.shiro.realms; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * @author 十里 * @description * @date 2021年04月23日 15:55 */ public class CustomerRealm extends AuthorizingRealm { /** * 授权 * * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } /** * 认证 * * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("==============================="); String principal = (String) token.getPrincipal(); if ("xiaochen".equals(principal)) { return new SimpleAuthenticationInfo(principal, "123", this.getName()); } return null; } }
-
-
首页及登录页
-
<%@page contentType="text/html; utf-8" pageEncoding="utf-8" isELIgnored="false" %> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h1>系统主页v1.0</h1> <a href="${pageContext.request.contextPath}/user/logout">退出登录</a> <ul> <li><a href="">用户管理</a></li> <li><a href="">商品管理</a></li> <li><a href="">订单管理</a></li> <li><a href="">物流管理</a></li> </ul> </body> </html>
-
<%@page contentType="text/html; utf-8" pageEncoding="utf-8" isELIgnored="false" %> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h1>用户登录</h1> <form action="${pageContext.request.contextPath}/user/login" method="post"> 用户名:<input type="text" name="username"/> <br/> 密码:<input type="password" name="password"/> <br/> <input type="submit" value="登录"/> </form> </body> </html>
-
-
因为项目中都以加入了权限控制以及用户认证,同时实现了用户退出登录功能。
-
demo中的用户权限及数据均为自定义的,未从数据库中获取,后面会编写整合mybatis的demo。
-