SpringBoot整合Shiro实现登录认证,鉴权授权

文章目录

  • 前言
  • 一、shiro简介
  • 二、环境搭建
    • 2.1.数据库
      • 2.1.1user用户表
      • 2.1.2user_role用户角色关系表
      • 2.1.3role角色表
      • 2.1.4role_permission角色权限关系表
      • 2.1.5permission权限表
    • 2.2导坐标
    • 2.3实体类
      • 2.3.1User
      • 2.3.2Role
      • 2.3.3Permission
    • 2.4MVC三层
      • 2.4.1User
        • 2.4.1.1mapper层
        • 2.4.1.2service层
        • 2.4.1.3controller层
      • 2.4.2Role
        • 2.4.2.1mapper层
        • 2.4.2.2service层
        • 2.4.2.3controller层
      • 2.3.3Permission
        • 2.4.3.1mapper层
        • 2.4.3.2service层
        • 2.4.3.3controller层
    • 2.5.加密工具类
    • 2.6.全局异常处理器
    • 2.7自定义Realm
    • 2.8shiro配置类
  • 三、案例演示
    • 3.1 登录认证
    • 3.2 权限认证
  • 总结


前言

近期对Springboot框架的学习中,为了更好的学习理解Springsecurity中间件,先学习了一下“老派”的shiro安全框架,本文章将通过注解的方式实现基础的用户认证和角色授权案例

一、shiro简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

二、环境搭建

2.1.数据库

2.1.1user用户表

在这里插入图片描述

2.1.2user_role用户角色关系表

在这里插入图片描述

2.1.3role角色表

在这里插入图片描述

2.1.4role_permission角色权限关系表

在这里插入图片描述

2.1.5permission权限表

在这里插入图片描述

2.2导坐标

<properties><java.version>1.8</java.version><!--shiro--><shiro.version>1.3.2</shiro.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!-- SECURITY begin --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency><!-- SECURITY end --></dependencies>

2.3实体类

2.3.1User

@TableName("pe_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {@TableId(value = "id",type = IdType.AUTO)private int id;private String username;private String password;private String salt;@TableField(exist = false)private Set<Role> roles;
}

2.3.2Role

@TableName("pe_role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;@TableField(exist = false)private Set<Permission> permissions;
}

2.3.3Permission

@TableName("pe_permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;
}

2.4MVC三层

2.4.1User

2.4.1.1mapper层

public interface UserMapper extends BaseMapper<User> {@Select("select * from pe_user where username = #{name}")User findUserByName(String name);//级联查询@Results({@Result(column = "id",property = "id"),@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(column = "salt",property = "salt"),@Result(column = "id",property = "roles",many=@Many(select = "com.apesource.shirostudy_demo_05.dao.RoleMapper.findRoleIdsByUserId"))})@Select("select * from pe_user where id = #{id}")User findUserDetailById(@Param("id") int id);
}

2.4.1.2service层

public interface IUserService extends IService<User> {User findUserByName(String name);User findUserDetailById(int id);
}@Service
@SuppressWarnings("all")
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements IUserService {@AutowiredUserMapper userMapper;@AutowiredIRoleService roleService;@Overridepublic User findUserByName(String name) {User user = userMapper.findUserByName(name);return user;}@Overridepublic User findUserDetailById(int id) {//根据userid查询userUser user = userMapper.findUserDetailById(id);//返回return user;}
}

2.4.1.3controller层

@RestController
public class UserController {@Autowiredprivate IUserService userService;//个人主页@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "访问个人主页成功";}//根据名字查询用户基本信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userByName/{name}")public String findUserByName(@PathVariable String name) {System.out.println(name);User user = userService.findUserByName(name);return user.toString();}//更新@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)public String update(@PathVariable String id) {return "更新用户成功";}//删除@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)public String delete(@PathVariable String id) {return "删除用户成功";}//根据id查询用户详细信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userById/{id}")public String findUserDetailById(@PathVariable int id) {return userService.findUserDetailById(id).toString();}//登录页面@RequestMapping(value = "/login")public String login(User user) {try {//1.构造登录令牌UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());//2.//2.获取subjectSubject subject = SecurityUtils.getSubject();//3.调用subject进行登录subject.login(token);return "登录成功!!!";} catch (AuthenticationException e) {return "用户名或密码错误!!!";}}//未登陆与未授权页面@RequestMapping(value = "/autherror")public String autherror() {return "未登录";}}

2.4.2Role

2.4.2.1mapper层

public interface RoleMapper extends BaseMapper<Role> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),@Result(column = "id",property = "permissions",many =@Many(select = "com.apesource.shirostudy_demo_05.dao.PermissionMapper.findPermissionIdsByRoleId"))})@Select("SELECT * FROM pe_role WHERE id IN (SELECT role_id FROM pe_user_role WHERE user_id = #{id})")Set<Role> findRoleIdsByUserId(int id);
}

2.4.2.2service层

public interface IRoleService extends IService<Role> {Set<Role> findRoleIdsByUserId(int id);
}
@Service
@SuppressWarnings("all")
public class RoleServiceImp extends ServiceImpl<RoleMapper, Role> implements IRoleService {@AutowiredRoleMapper roleMapper;@AutowiredIPermissionService permissionService;@Overridepublic Set<Role> findRoleIdsByUserId(int id) {Set<Role> roles = roleMapper.findRoleIdsByUserId(id);return roles;}
}

2.4.2.3controller层

由于不演示,无。

2.3.3Permission

2.4.3.1mapper层

public interface PermissionMapper extends BaseMapper<Permission> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),})@Select("SELECT * FROM pe_permission WHERE id IN (SELECT permission_id FROM pe_role_permission WHERE role_id = #{id} ) ")Set<Permission> findPermissionIdsByRoleId(int id);
}

2.4.3.2service层

public interface IPermissionService extends IService<Permission> {Set<Permission> findPermissionIdsByRoleId(int id);
}
@Service
public class PermissionServiceImp extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {@Autowired(required = false)PermissionMapper permissionMapper;@Overridepublic Set<Permission> findPermissionIdsByRoleId(int id) {Set<Permission> permissionIds = permissionMapper.findPermissionIdsByRoleId(id);return permissionIds;}
}

2.4.3.3controller层

由于不演示,无。

2.5.加密工具类

public class DigestsUtil {public static final String SHA1 = "SHA-1";public static final Integer COUNTS =369;/*** @Description sha1方法* @param input 需要散列字符串* @param salt 盐字符串* @return*/public static String show(String input, String salt) {return new SimpleHash(SHA1, input, salt,COUNTS).toString();}/*** @Description 随机获得salt字符串* @return*/public static String generateSalt(){SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();return randomNumberGenerator.nextBytes().toHex();}/*** @Description 生成密码字符密文和salt密文* @param* @return*/public static Map<String,String> entryptPassword(String passwordPlain) {Map<String,String> map = new HashMap<>();String salt = generateSalt();String password =show(passwordPlain,salt);map.put("salt", salt);map.put("password", password);return map;}
}

2.6.全局异常处理器

@ControllerAdvice
public class BaseExceptionHandler {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e){return "未授权!!!";}
}

2.7自定义Realm

public class MyRealm extends AuthorizingRealm {@AutowiredIUserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {int userId = (int) principalCollection.getPrimaryPrincipal();User user = userService.findUserDetailById(userId);Set<String> roles = new HashSet<>();Set<String> permissions = new HashSet<>();user.getRoles().stream().forEach(role -> {roles.add(role.getCode());role.getPermissions().stream().forEach(permission -> {permissions.add(permission.getCode());});});SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addRoles(roles);info.addStringPermissions(permissions);return info;}/*** 认证方法*  参数:传递的用户名密码*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.获取登录的用户名密码(token)UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//2.根据用户名查询数据库//mybatis情景下:user对象中包含ID,name,pwd(匿名)User user = userService.findUserByName(username);if(user != null){SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"myRealm");return info;}return null;}/*** @Description 自定义密码比较器* @param* @return* bean标签 init-method属性*/@PostConstructpublic void initCredentialsMatcher() {//指定密码算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);//指定迭代次数hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);//生效密码比较器setCredentialsMatcher(hashedCredentialsMatcher);}
}

2.8shiro配置类

@Configuration
public class ShiroConfiguration {/*** 1.创建shiro自带cookie对象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.创建realm@Beanpublic MyRealm getRealm(){return new MyRealm();}/*** 3.创建会话管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.创建安全管理器@Beanpublic SecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保证实现了Shiro内部lifecycle函数的bean执行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.开启对shiro注解的支持*   AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}//8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制@Beanpublic ShiroFilterFactoryBean shiroFilter() {//1.创建过滤器工厂ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();//2.设置安全管理器filterFactory.setSecurityManager(defaultWebSecurityManager());//3.通用配置(跳转登录页面,为授权跳转的页面)filterFactory.setLoginUrl("/autherror");//跳转url地址//4.设置过滤器集合//key = 拦截的url地址//value = 过滤器类型Map<String,String> filterMap = new LinkedHashMap<>();//key:请求规则   value:过滤器名称filterMap.put("/login","anon");//当前请求地址可以匿名访问filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问//在过滤器工程内设置系统过滤器filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}}

三、案例演示

用户密码均是123456加密后保存至数据库

3.1 登录认证

成功登录:
在这里插入图片描述
失败登录:
在这里插入图片描述

3.2 权限认证

由于刚刚登录的信息有管理员和员工的身份,所以其所有功能都能使用。
有权限时:在这里插入图片描述
无权限时:在这里插入图片描述
无权限时会被全局异常处理器拦截,并作出响应的响应。


总结

通过对shrio的学习,更好的理解了安全认证以及鉴权授权的流程,大概了解了安全认证的机制,对我之后学习理解Springsecurity有更好的帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git 删除 GitHub仓库的文件

新建文件夹 git bash here 在新建的文件夹里右键git bash here打开终端&#xff0c;并执行git init初始化仓库 git clone <你的地址> 找到github上要删除的仓库地址&#xff0c;并复制&#xff0c;在终端里输入git clone <你的地址> 要删除文件的库里右键git b…

归并排序(C++ mpi 并行实现)

文章目录 主要思路1. 串行归并排序2. 进程的分发3. 对接收到的子数组进行排序4. 合并数组5.输出排序后的数组6.进程分发部分的优化7.完整代码 主要思路 我们首先实现串行的归并排序&#xff1b;实现进程的分发&#xff1b;排序其中的每个子部分&#xff1b;进程的合并通信&…

理解 Go 中的切片:append 操作的深入分析(篇2)

理解 Go 语言中 slice 的性质对于编程非常有益。下面&#xff0c;我将通过代码示例来解释切片在不同函数之间传递并执行 append 操作时的具体表现。 本篇为第 2 篇&#xff0c;当切片的容量 cap 不够时 func main() {// slice1 当前长度为 3&#xff0c;容量大小也为 3slice1 :…

.netcore grpc的proto文件字段详解

一、.proto文件字段概述 grpc的接口传输参数都是根据.proto文件约定的字段格式进行传输的grpc提供了多种类型字段&#xff1b;主要包括标量值类型&#xff08;基础类型&#xff09;、日期时间、可为null类型、字节、列表、字典、Any类型&#xff08;任意类型&#xff09;、One…

前端笔试+面试分享

以下是个人线下面试遇到的真实的题&#xff0c;仅供参考和学习 1. css 选择符有哪些&#xff1f;哪些属性可以继承&#xff1f;优先级算法加何计算&#xff1f; CSS选择符有很多种&#xff0c;例如类型选择器、类选择器、ID选择器、属性选择器、伪类选择器、伪元素选择器等。 …

Algorithem Review 5.2 图论

网络流 设源点为 s s s&#xff0c;汇点为 t t t&#xff0c;每条边 e e e 的流量上限为 c ( e ) c(e) c(e)&#xff0c;流量为 f ( e ) f(e) f(e)。割 指对于某一顶点集合 P ⊂ V P \subset V P⊂V&#xff0c;从 P P P 出发指向 P P P 外部的那些原图中的边的集合&a…

回归预测 | MATLAB实现基于SSA-KELM-Adaboost麻雀算法优化核极限学习机结合AdaBoost多输入单输出回归预测

回归预测 | MATLAB实现基于SSA-KELM-Adaboost麻雀算法优化核极限学习机结合AdaBoost多输入单输出回归预测 目录 回归预测 | MATLAB实现基于SSA-KELM-Adaboost麻雀算法优化核极限学习机结合AdaBoost多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本…

SSH远程连接MacOS catalina并进行终端颜色配置

一、开关SSH服务 在虚拟机上安装了MacOS catalina&#xff0c;想要使用SSH远程进行连接&#xff0c;但是使用“系统偏好设置”/“共享”/“远程登录”开关进行打开&#xff0c;却一直是正在启动“远程登录”&#xff1a; 难道是catalina有BUG&#xff1f;不过还是有方法的&…

第07天 Static关键字作用及用法

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a;每天一个知识点 ✨特色专栏&#xff1a…

【前端|Javascript第5篇】全网最详细的JS的内置对象文章!

前言 在当今数字时代&#xff0c;前端技术正日益成为塑造用户体验的关键。我们在开发中需要用到很多js的内置对象的一些属性来帮助我们更快速的进行开发。或许你是刚踏入前端领域的小白&#xff0c;或者是希望深入了解内置对象的开发者&#xff0c;不论你的经验如何&#xff0c…

MATLAB中的代数环概念

在 Simulink 模型中&#xff0c;当存在信号环并且信号环中只存在直接馈通模块时&#xff0c;将出现代数环。直接馈通表示 Simulink 需要模块输入信号的值来计算当前时间步的输出。这种信号循环会在同一时间步中产生模块输出和输入的循环依存关系。这会导致一个需要在每个时间步…

【【verilog典型电路设计之流水线结构】】

verilog典型电路设计之流水线结构 下图是一个4位的乘法器结构&#xff0c;用verilog HDL 设计一个两级流水线加法器树4位乘法器 对于流水线结构 其实需要做的是在每级之间增加一个暂存的数据用来存储 我们得到的东西 我们一般来说会通过在每一级之间插入D触发器来保证数据的联…

OpenCV-Python中的图像处理-图像特征

OpenCV-Python中的图像处理-图像特征 图像特征Harris角点检测亚像素级精度的角点检测Shi-Tomasi角点检测SIFT(Scale-Invariant Feature Transfrom)SURF(Speeded-Up Robust Features)FAST算法BRIEF(Binary Robust Independent Elementary Features)算法ORB (Oriented FAST and R…

python编程中有哪些方便的调试方法

大家好&#xff0c;给大家分享一下一个有趣的事情&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 对于每个程序开发者来说&#xff0c;调试几乎是必备技能。常用Pycharm编辑器里的方法有Print大法、log大法&#xff0c;但缺少类似Matlab的…

怎么开通Tik Tok海外娱乐公会呢?

TikTok作为全球知名的社交媒体平台&#xff0c;吸引了数亿用户的关注和参与。许多公司和个人渴望通过开通TikTok直播公会进入这一领域&#xff0c;以展示自己的创造力和吸引更多粉丝。然而&#xff0c;成为TikTok直播公会并非易事&#xff0c;需要满足一定的门槛和申请找cmxyci…

【日常积累】Linux之init系统学习

init系统简介: Linux 操作系统的启动首先从 BIOS 开始&#xff0c;接下来进入 boot loader&#xff0c;由 bootloader 载入内核&#xff0c;进行内核初始化。内核初始化的最后一步就是启动 pid 为 1 的 init 进程&#xff0c;这个进程是系统的第一个进程&#xff0c;它负责产生…

银河麒麟服务器v10 sp1 .Net6.0 上传文件错误

上一篇&#xff1a;银河麒麟服务器v10 sp1 部署.Net6.0 http https_csdn_aspnet的博客-CSDN博客 .NET 6之前&#xff0c;在Linux服务器上安装 libgdiplus 即可解决&#xff0c;libgdiplus是System.Drawing.Common原生端跨平台实现的主要提供者&#xff0c;是开源mono项目。地址…

ubuntu 部署 ChatGLM-6B 完整流程 模型量化 Nvidia

ubuntu 部署 ChatGLM-6B 完整流程 模型量化 Nvidia 初环境与设备环境准备克隆模型代码部署 ChatGLM-6B完整代码 ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#x…

力扣 322. 零钱兑换

题目来源&#xff1a;https://leetcode.cn/problems/coin-change/description/ C题解&#xff08;来源代码随想录&#xff09;&#xff1a;题目中说每种硬币的数量是无限的&#xff0c;可以看出是典型的完全背包问题。动规五部曲分析如下&#xff1a; 确定dp数组以及下标的含义…

原码、反码、补码,进制转换,有符号数和无符号数转换

计算机底层存储数据时&#xff0c;存储的是数据对应的二进制数字。对于整型数据&#xff0c;其二进制表示形式有三种&#xff0c;分别是&#xff1a;原码、反码、补码&#xff0c;而实际存储的是整型数据的补码。 原码、反码以及补码都是有符号的&#xff0c;其中最高位存放符…