高斋晓开卷,独共圣人语。这篇文章主要讲述#yyds干货盘点#Springboot——Shiro(安全框架)学习笔记相关的知识,希望能为你提供帮助。
Shiro
shiro的核心 Securit Manager
认证:
身份认证,就是判断一个用户是否为合法用户的处理过程,最简单的身份认证就是系统通过核对用户输入的用户名和口令(密码) ,看是否和系统储存中的该用户的用户名和口令一致,来判断用户身份是否正确,
shiro中认证的关键的对象
Subject :主体, 访问系统的用户,主体可以是用户,程序,进行认证的都称为主体
Principal:身份信息 ,进行身份认证的标识,标识必须具有唯一性,如用户名,手机号,邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份。
credential:凭证信息 ,是只有主体自己知道的安全信息,如密码,证书等;
shiro的配置文件: shiro的配置文件是.ini格式,这个配置文件目前我们主要是用来编写用户的权限,可以理解为模拟场景,在真实使用时,将会切换到数据库中使用
第一个shiro的程序:
//1、创建安全管理器
DefaultSecurityManager securityManager=new DefaultSecurityManager();
//2、给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3、SecurityUtils 全局安全工具类,给全局安全工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
// 4、关键对象subJect 主体
Subject subject=SecurityUtils.getSubject();
// 5、创建令牌
UsernamePasswordToken token=new UsernamePasswordToken("xiaochen","123");
这个没有写完,只是介绍一下基本步骤
shiro抛出的异常代表着什么
try
subject.login(token);
System.out.println("认证状态"+subject.isAuthenticated());
catch (UnknownAccountException e)
e.printStackTrace();
System.out.println("用户不存在");
catch (IncorrectCredentialsException e)
e.printStackTrace();
System.out.println("密码错误");
如果需要把数据改到数据库中读取的话,我们需要自己写一个类去继承AuthorizingRealm,
需要重写其中的两个类, 其中一个是认证,一个是授权
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
return null;
认证的代码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
String principal= (String)authenticationToken.getPrincipal();
System.out.println(principal);
// 这里就得到了身份信息,然后更加身份信息去jdbc里面查询相关的数据库了
if("xiaochen".equals(principal))
//参数1: 代表数据库的正确的用户名
//参数2:代表数据库中的密码
//参数3: 提供当前reaml的名字 this.getName
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo("xiaochen","123",this.getName());
return simpleAuthenticationInfo;
return null;
加密
MD5算法
作用:一般是用来加密或者签名,
特点: 算法不可逆,无论相同内容执行多少次md5生成的结果都是一致
md5还可以用来判断文件是否一致,将两个文件进行Md5加密,然后比对密码是否一致
生成结果始终是一个16进制,32位长度的字符串
public class TestShiroMD5
public static void main(String[] args)
Md5Hash md5Hash=new Md5Hash("zxdmwy"); // 进行md5加密
System.out.println(md5Hash.toHex()); //输出加密后的结果
// 使用md5+盐(salt)
Md5Hash md5Hash1=new Md5Hash("123","123"); //md5+盐第一个参数是密文,第二个是盐值
System.out.println(md5Hash1.toHex()); //输出加密后的结果
//使用Md5+盐+hash散列 后面的数字,我们一般使用1024或者2048
Md5Hash md5Hash2=new Md5Hash("123","123",1024);
System.out.println(md5Hash2.toHex()); //输出加密后的结果
在shiro中使用md5加密认证的方式,
需要重写realm
/**
* 使用自定义的realm 加入md5+salt+hash
*/
public class CustomerMd5Realm extends AuthorizingRealm
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
// 获取身份信息
String principl=(String)authenticationToken.getPrincipal();
if("xiaochen".equals(principl))
//需要注意,这里的第二个参数是md5加密后的密码
return new SimpleAuthenticationInfo(principl,"9c3b5c0672cd599ccf1019bddaa8089b", ByteSource.Util.bytes("yan"),this.getName()); //如果是用盐,这里就需要在加一个参数了,这个盐需要通过ByteSource.Util.bytes("yan")这个方法
return null;
然后应用
授权
public class TestCustomerMd5RealmAuthenicator
public static void main(String[] args)
// 创建安全管理器
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
// 注入刚刚写的md5realm
CustomerMd5Realm customerMd5Realm = new CustomerMd5Realm();
// 设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 使用算法
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
hashedCredentialsMatcher.setHashIterations(1024);
customerMd5Realm.setCredentialsMatcher(hashedCredentialsMatcher);
//注入
defaultSecurityManager.setRealm(customerMd5Realm);
//将安全管理器注入安全工具
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject=SecurityUtils.getSubject();
//认证
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen","123");
try
subject.login(token);
System.out.println("登录成功");
catch (UnknownAccountException e)
e.printStackTrace();
System.out.println("用户名不存在");
catch (AuthenticationException e)
e.printStackTrace();
System.out.println("密码错误");
是对认证后的用户就行权限控制,即控制谁可以访问哪些资源,主体经过身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
关键对象
授权可简单理解为who 对what进行how操作
who 主体 主体需要访问系统中的资源
what 资源,如系统菜单、页面、按钮、类方法、系统商品信息等,,资源又包括资源类型和资源实例,比如商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息,也属于资源实例。
How 权限/许可 规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限,用户添加权限,某个类方法的调用权限,
授权方式
基于角色的访问控制
RBAC基于角色的访问控制,是以角色为中心进行访问控制
if(subject.hasRole("admin")) //什么资源
就是什么样的角色就可以操作什么样的资源
基于资源的访问控制
RBAC基于资源的访问控制,是以资源为中心进行访问控制
if(subject.isPermission("user:create:*")) //"user:create:*" 可以理解为,subject这个主体 具有user模块下的 *资源,具有创建权限
权限字符串
权限字符串的规则是: "资源标识符:操作:资源实例标识符" 意思是对那个资源的那个实例具有什么操作,权限字符串也可以使用*通配符
例子 :
用户创建权限 :user:create 或者user:create:*
用户修改实例001的权限 :user:update:001
用户实例001的所有权限: user:*:001
授权实现方式
编程式
Subject subject =SecurityUtils.getSubject();
if(subject.hasRole("admin"))
//有权限
else
//无权限
注解式
RequiresRoless("admin")
public void hello()
//有权限
标签式
JSP/GSP标签:在JSP/GSP页面通过相应的标签完成:
< shiro:hasRole name="admin">
< !-有权限->
< /shiro :hasRole>
注意:Thymeleaf中使用shiro需要额外集成!
shiro整合spingboot
引入依赖
< dependency>
< groupId> org.apache.shiro< /groupId>
< artifactId> shiro-spring-boot-starter< /artifactId>
< version> 1.5.3< /version>
< /dependency>
shiro常见的过滤器
首先创建springboot项目,在项目配置包。然后在配置包下,创建shiro配置类
package work.zx.config;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
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 work.zx.shiro.realms.CustomerRealm;
import java.util.HashMap;
import java.util.Map;
/**
* 用来整合shiro框架相关的配置类
*/
@Configuration
public class ShiroConfig
// 1、创建shiroFilter// 负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFitlerFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager)
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 配置系统受限资源
// 配置系统公共资源
Map< String,String> map= new HashMap< String,String> ();
map.put("/login","anon"); //不受限的资源要放上面
map.put("/index","authc"); // 请求这个资源需要认证和授权 //路径可以设置通配符
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
// 2、创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm realm)
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
//给安全管理器设置
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
// 3、创建自定义realm
@Bean
public Realm getRealm()
return new CustomerRealm();
然后创建shiro包。在包下创建realms包,我们的自定义reaml类就放这个下面
package work.zx.shiro.realms;
import lombok.val;
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;
/**
* 自定义realm
*/
public class CustomerRealm extends AuthorizingRealm
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
return null;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException
System.out.println("=============");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo();
String principal=(String) authenticationToken.getPrincipal();
if("xiaochen".equals(principal))
return new SimpleAuthenticationInfo(principal,"123",this.getName());
return null;
然后创建controller包,并创建一个controller类
package work.zx.controller;
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.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* < p>
*前端控制器
* < /p>
*
* @author 摘星
* @since 2021-02-02
*/
@Controller
public class User1Controller
@RequestMapping("/","/index")
public String index(Model model)
return "index";
@RequestMapping("/toLogin")
public String toLogin()
return "login";
@RequestMapping("/toRegin")
public String toRegin()
return "login";
@RequestMapping("/login")
public String login(String id,String password,Model model)
// 获取当前用户
Subject subject= SecurityUtils.getSubject();
//封装用户登录数据
UsernamePasswordToken token=new UsernamePasswordToken(id,password);
try
subject.login(token); //执行登录方法, 如果没有异常,就说明ok了
return "index";
catch (UnknownAccountException e)
// 用户名不存在
model.addAttribute("msg","用户名错误");
return "login";
catch (IncorrectCredentialsException e)
model.addAttribute("msg","密码错误");
return "login";
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized()
return "未经授权,无法访问";
需要注意的是,如果换成数据库登录,并且使用了md5加密,那么需要修改一下配置类,在自定义realm中加入以下
// 3、创建自定义realm
@Bean
public Realm getRealm()
CustomerRealm customerRealm = new CustomerRealm();
//修改凭证效验匹配器
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//设置算法为
credentialsMatcher.setHashAlgorithmName("md5");
// 设置散列次数
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
return customerRealm;
授权
可以通过注解、标签、编码实现,权限控制
1、编码
@RequestMapping("save")
public String save()
//获取主体对象
Subject subject= SecurityUtils.getSubject();
if(subject.hasRole("admin"))
System.out.println("保存");
else
System.out.println("无权访问");
return "redirect:/index";
2、注解
@RequiresRoles(value="https://www.songbingjia.com/android/admin","user")//同时具有多个角色
@RequiresPermissions() //用了判断权限字符串
@RequestMapping("save") @RequiresRoles("admin") //用来判断角色,表示只有user角色才能使用 public String save() //获取主体对象 //Subject subject= SecurityUtils.getSubject(); //if(subject.hasRole("admin")) //System.out.println("保存"); //else //System.out.println("无权访问"); // System.out.println("成功"); return "redirect:/index";
权限表设计
使用CacheManager做缓存
Cache 缓存 计算机内存中的一段数据
作用: 用来减轻db的访问压力,从而提高系统的查询效率
流程 :
引入shiro和ehcache依赖,
< dependency>
< groupId> org.apache.shiro< /groupId>
< artifactId> shiro-ehcache< /artifactId>
< version> 1.5.3< /version>
< /dependency>
在shiro的配置类中开启自定义缓存
【#yyds干货盘点#Springboot——Shiro(安全框架)学习笔记】
// 3、创建自定义realm
@Bean
public Realm getRealm()
CustomerRealm customerRealm = new CustomerRealm();
//修改凭证效验匹配器
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
//设置算法为到
credentialsMatcher.setHashAlgorithmName("md5");
// 设置散列次数
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
//开启缓存管理,
customerRealm.setCacheManager(new EhCacheManager());
customerRealm.setCachingEnabled(true); //开启全局缓存
customerRealm.setAuthenticationCachingEnabled(true); //开启认证缓存
customerRealm.setAuthenticationCacheName("authenticationCache");
customerRealm.setAuthorizationCachingEnabled(true); //开启授权缓存
customerRealm.setAuthorizationCacheName("authorizationCache");
return customerRealm;
推荐阅读
- #私藏项目实操分享#?Alibaba中间件技术系列「EasyExcel实战案例」实战研究一下EasyExcel如何从指定文件位置进行读取数据
- 学习Java必备的基础知识打卡12.19,要想学好必须扎实基本功(?建议收藏)#yyds干货盘点#
- #yyds干货盘点# 数据结构与算法之单链表
- #yyds干货盘点# 滚雪球学 Python 之怎么玩转时间和日期库
- Linux系统目录名称命名规则以及用途总结
- 项目不用数据库实现留言板(用本地文件)#yyds干货盘点#
- #yyds干货盘点#内核编译和管理
- #yyds干货盘点# Redis扩展数据类型详解
- K8SReplicaSet