SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期
写在前面
通过前几篇文章的学习,我们从大体上了解了shiro关于认证和授权方面的应用。在接下来的文章当中,我将通过一个demo,带领大家搭建一个SpringBoot整合Shiro的一个项目开发脚手架,将之前学过的知识点串到一起,其中,也会补充一些之前没有讲过的内容。通过这个demo结束这几天的学习,同时也是结束国庆中秋小长假shiro系列专题入门文章。
SpringBoot整合Shiro思路分析
鉴权流程分析
我们将我们的SpringBoot应用整合shiro,主要目的就是让shiro帮我们处理认证和授权的相关内容。也就是说,我们需要让shiro接管我们SpringBoot应用的会话。让用户的每一次请求都经过shiro进行认证和授权。因此,我们需要将用户请求拦截下来转发给shiro处理,这个拦截器是shiro提供的,ShiroFilter
。
步骤:
用户通过客户端(浏览器、手机App、小程序)发起请求
ShiroFilter拦截请求并判断请求访问的资源是否为受保护资源:
2.1 是,则执行步骤3
2.2 不是,则直接放行
判断用户是否已通过认证:
3.1 是 ,则执行步骤4
3.2 否,将用户请求重定向到认证页面,让用户先认证
获取用户权限信息和访问资源所需要的权限信息进行比对:
4.1 用户具备访问权限,则放行
4.2 用户不具备权限,返回403的相应提示
数据库分析设计
我们通过MySQL保存我们的认证和权限的相关数据。采用用户-角色-权限模型实现动态管理用户权限信息。
我们将系统当中的菜单、按钮、后端接口都抽象成系统的资源数据。以下是数据库表的设计:
文末提供sql脚本的下载。
整合步骤
环境搭建
maven
创建一个SpringBoot的web应用,并引入如下依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.6.0</version>
</dependency>
添加对用户、角色和资源的CRUD支持
这里代码就省略了,不影响理解,完整代码可以从文末提供的方式中下载。
配置Shiro
自定义Realm
/**自定义Realm,使用mysql数据源
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/6 9:09
*/
public class MySQLRealm extends AuthorizingRealm {
@Autowired
private IUserService userService;
@Autowired
private IRoleService roleService;
@Autowired
private IResourceService resourceService;
/**
* 授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
List<Role> roleList = roleService.findByUsername(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
for (Role role : roleList) {
authorizationInfo.addRole(role.getRoleName());
}
List<Long> roleIdList = new ArrayList<>();
for (Role role : roleList) {
roleIdList.add(role.getRoleId());
}
List<Resource> resourceList = resourceService.findByRoleIds(roleIdList);
for (Resource resource : resourceList) {
authorizationInfo.addStringPermission(resource.getResourcePermissionTag());
}
return authorizationInfo;
}
/**
* 认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
if(token==null){
return null;
}
String principal = (String) token.getPrincipal();
User user = userService.findByUsername(principal);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
return simpleAuthenticationInfo;
}
}
shiro中的Realm对象充当了认证、授权数信息的据源作用。关于更多自定义Realm的内容请参考我的另一篇文章《Shiro入门学习---使用自定义Realm完成认证|练气中期》 。
ShiroConfig
/**shiro配置类
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/6 9:11
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilter拦截器
* @return ShiroFilterFactoryBean
*/
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//配置不拦截路径和拦截路径,顺序不能反
HashMap<String, String> map = new HashMap<>(5);
map.put("/authc/**","anon");
map.put("/login.html","anon");
map.put("/js/**","anon");
map.put("/css/**","anon");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//覆盖默认的登录url
shiroFilterFactoryBean.setLoginUrl("/authc/unauthc");
return shiroFilterFactoryBean;
}
@Bean
public Realm getRealm(){
//设置凭证匹配器,修改为hash凭证匹配器
HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
//设置算法
myCredentialsMatcher.setHashAlgorithmName("md5");
//散列次数
myCredentialsMatcher.setHashIterations(512);
MySQLRealm realm = new MySQLRealm();
realm.setCredentialsMatcher(myCredentialsMatcher);
return realm;
}
/**
* 创建shiro web应用下的安全管理器
* @return DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getSecurityManager(Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
SecurityUtils.setSecurityManager(securityManager);
return securityManager;
}
}
在编写shiro配置类这一步,需要大家注意的是,因为我们使用的是md5+salt+hash加密我们的密码,因此要换掉默认的凭证匹配器CredentialsMatcher对象,对于这部分的内容请参考我的另一篇文章《shiro入门学习--使用MD5和salt进行加密|练气后期》 。
实现认证模块
VO层
/**认证请求参数
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/7 15:12
*/
@Data
public class LoginVO implements Serializable {
private String username;
private String password;
}
web层
/**认证模块
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/6 10:07
*/
@RestController
@RequestMapping("/authc")
public class AuthcController {
@Autowired
private AuthcService authcService;
@PostMapping("/login")
public boolean login(@RequestBody LoginVO loginVO){
return authcService.login(loginVO);
}
@GetMapping("/unauthc")
public String unauthc(){
return "请先登录";
}
}
service层
/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/7 15:15
*/
@Service
public class AuthcServiceImpl implements AuthcService {
@Override
public boolean login(LoginVO loginVO) throws AuthenticationException {
if (loginVO==null){
return false;
}
if (loginVO.getUsername()==null||"".equals(loginVO.getUsername())){
return false;
}
if (loginVO.getPassword() == null || "".equals(loginVO.getPassword())){
return false;
}
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginVO.getUsername(), loginVO.getPassword());
subject.login(token);
return true;
}
}
实现产品模块
/**产品模块
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/6 10:14
*/
@RestController
@RequestMapping("/product")
public class ProductController {
@RequiresPermissions("product:get")
@GetMapping("/get/list")
public String getProductList() {
return "productList";
}
@RequiresPermissions("product:delete")
@GetMapping("/delete")
public String deleteProduct() {
return "删除产品数据";
}
}
对于注解实现访问控制,shiro主要有两个注解:RequiresPermissions和RequiresRoles。均可以用在类和方法上。具体用在哪可以根据自己的系统权限划分粒度决定。
对于这两个注解,有两个参数:
value
:分别对应permission的权限字符串值和role的角色名称;logical
:逻辑运算符。这是一个枚举类型,有AND
和OR
两个值。当使用AND
时表示需要满足所有传入的value
值,OR
表示仅需满足一个value
即可。默认为AND
关于shiro权限(访问控制)的更多内容,可以阅读我的另一篇文章《shiro入门学习--授权(Authorization)|筑基初期》
简单测试
认证通过的情况
认证未通过的情况
获取产品信息
请求没有访问权限的资源
默认的消息提示可以换一下。
未经过认证直接访问受保护资源
写在最后
在这一篇文章当中,我们搭建了一个初步的SpringBoot整合Shiro的应用,实现了认证和授权。
在下一篇文章当中,我们将接着完善这个小demo。加入自定义shiro会话管理和shiro缓存的内容。并且会将这个小demo进行升级,使其变成前后端分离的模式。
双节即将结束,代码人在江湖!
如果您觉得这篇文章能给您带来帮助,那么可以点赞鼓励一下。如有错误之处,还请不吝赐教。在此,谢过各位乡亲父老!
代码及sql下载方式:微信搜索【Java开发实践】,加关注并回复20201007
即可获取下载链接。
SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期的更多相关文章
- SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理|前后端分离(下)----筑基后期
写在前面 在上一篇文章<SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期>当中,我们初步实现了SpringBoot整合Shiro ...
- SpringBoot整合Shiro完成认证
三.SpringBoot整合Shiro思路 首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如 ...
- SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)
导入依赖(pom.xml) <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...
- SpringBoot整合Shiro 四:认证+授权
搭建环境见: SpringBoot整合Shiro 一:搭建环境 shiro配置类见: SpringBoot整合Shiro 二:Shiro配置类 shiro整合Mybatis见:SpringBoot整合 ...
- SpringBoot整合Shiro权限框架实战
什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...
- SpringBoot整合Shiro实现权限控制
目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...
- SpringBoot系列十二:SpringBoot整合 Shiro
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Shiro 2.具体内容 Shiro 是现在最为流行的权限认证开发框架,与它起名的只有最初 ...
- SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建
SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...
- 转:30分钟了解Springboot整合Shiro
引自:30分钟了解Springboot整合Shiro 前言:06年7月的某日,不才创作了一篇题为<30分钟学会如何使用Shiro>的文章.不在意之间居然斩获了22万的阅读量,许多人因此加了 ...
随机推荐
- 热更新 && 增量更新
Unity中SLua.Tolua.XLua和ILRuntime效率评测 http://blog.csdn.net/u011467512/article/details/72716376 如何阅读lua ...
- MonoBehaviour生命周期与对象数据池应用
预热游戏对象: tempObject = Instantiate(cubePrefab) as GameObject ; tempObject .SetActive( false ); 游戏对象tem ...
- vue 在使用数组的时候,数组内部数据发生变化,视图却没有改变
data(){ return{ todos: [ {name: 'aa', age: 22}, {name: 'bb', age: 23} ] } } methods:{ changeTodos(){ ...
- Codeforces Round #668 (Div. 2)A-C题解
A. Permutation Forgery 题目:http://codeforces.com/contest/1405/problem/A 题解:这道题初看有点吓人,一开始居然想到要用全排序,没错我 ...
- 跟着尚硅谷系统学习Docker-【day02】
day02-20200714 p9.docker阿里云配置 helloword 拉取镜像-运行- 拉取镜像如果从国外网站拉取辉比较慢,所以需要配置阿里云或者网易云得镜像仓库. 首先 ...
- 求支付表中按id累积和最接近100的那条记录
此例源自美团的一道SQL面试题 支付表结构: create table hy_payment( id number(4,0) primary key, pay number(3,0) not null ...
- java中数据类型占多少字节
基本类型(primitive type) 数值类型:byte占1个字节:short占2个字节:int占4个字节:long占8个字节:float占4个字节:double占8个字节.char占2个字节. ...
- ASP.NET Core 性能优化最佳实践
本文提供了 ASP.NET Core 的性能最佳实践指南. 译文原文地址:https://docs.microsoft.com/en-us/aspnet/core/performance/perfor ...
- 小白的springboot之路(十七)、阿里云OSS 的使用
0-前言 项目中,文件服务必不可少,常用的有各云服务商的OSS服务(如阿里云OSS,腾讯云OSS,七牛云).自建(fastDFS.minio): 推荐:如果用云服务的话,阿里云OSS很方便,如果自建的 ...
- 【python】列表与数组之间的相互转换
安装numpy pip3 install numpy 列表转数组 np.array() import numpy as np a = [1, 2, 3] b = np.array(a) 列表转数组 a ...