前言
shiro是一个轻量级的权限框架,该篇我将会从0到1快速教大家搭建出一套包含角色,权限登录校验的项目。
就算你没了解过,也能学会。跟着我把代码敲一遍,这个项目就是属于你的。
该篇文章比较啰嗦,篇幅较长,如果不是入门的初学者大可不必从头开始阅读(我一般的教程都会以从零开始的方式,所以都比较啰嗦)。
正文
数据库的准备(Mysql):
在这是实践的案例里面,数据库表三张核心表,两张中间表(如果你表太多也可以自己调整,我这边是严格细分了):
数据库名:
my_system
核心表1 帐号表 , sys_user 表:
对应的mysql语句:
CREATE TABLE `sys_user` ( `userId` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id 作为表主键 用于关联', `userName` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户登录帐号', `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户登录密码', `userRemarks` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注,预留字段', PRIMARY KEY (`userId`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 20002 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
核心表2 角色表,sys_role 表:
对应的mysql语句:
CREATE TABLE `sys_role` ( `roleId` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT ' 角色id 作为表主键 用于关联', `roleName` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名', `roleRemarks` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注,预留字段', PRIMARY KEY (`roleId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
核心表3 权限表,sys_permissions 表:
对应的mysql语句:
CREATE TABLE `sys_permissions` ( `perId` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限表id 作为表主键 用于关联', `permissionsName` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名称', `perRemarks` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注,预留字段', PRIMARY KEY (`perId`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
中间表 1 帐号与角色的中间表,user_role 表:
对应的mysql语句:
CREATE TABLE `user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '表主键id', `userId` int(11) NULL DEFAULT NULL COMMENT '帐号表的主键id', `roleId` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色表的主键id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
中间表 2 角色与权限的中间表,role_per 表:
对应的mysql语句:
CREATE TABLE `role_per` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '表主键id', `roleId` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色表的主键id', `perId` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限表的主键id', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
对这些表做个简单描述:
首先是 帐号表 sys_user ,里面存放的就是 登录系统的帐号和密码;
角色表 sys_role ,里面存放的就是 角色信息;
权限表 sys_permissions ,里面存放的就是 权限信息;
中间表 user_role 和 role_per,分别是存放 哪个用户分配的哪个角色,多对多; 哪个角色对应哪个权限,多对多;
数据库方面的设计就到此,那么在开始代码实现前,我们还继续一下数据库方面的东西,就是数据模拟;
也就是说,这个项目实现后,我们的数据情况是这样的:
帐号表,注册了两个帐号进去(remark字段是个预留字段,可以无视):
sys_user
相关sql:
INSERT INTO `my_system`.`sys_user`(`userId`, `userName`, `password`, `userRemarks`) VALUES (10001, 'adminHong', '202cb962ac59075b964b07152d234b70', '小红'); INSERT INTO `my_system`.`sys_user`(`userId`, `userName`, `password`, `userRemarks`) VALUES (20001, 'jc', 'e165421110ba03099a1c0393373c5b43', 'JC');
然后这个系统暂时已经创建了2个角色,分别是管理员和普通用户:
sys_role
相关sql:
INSERT INTO `my_system`.`sys_role`(`roleId`, `roleName`, `roleRemarks`) VALUES ('100', 'admin', '系统管理员'); INSERT INTO `my_system`.`sys_role`(`roleId`, `roleName`, `roleRemarks`) VALUES ('200', 'common', '普通用户');
然后是这个系统具体的权限菜单或者说是权限按钮:
sys_permissions
相关sql:
INSERT INTO `my_system`.`sys_permissions`(`perId`, `permissionsName`, `perRemarks`) VALUES ('M01', 'resetPassword', '重置密码'); INSERT INTO `my_system`.`sys_permissions`(`perId`, `permissionsName`, `perRemarks`) VALUES ('M02', 'querySystemLog', '查看系统日志'); INSERT INTO `my_system`.`sys_permissions`(`perId`, `permissionsName`, `perRemarks`) VALUES ('M03', 'exportUserInfo', '导出用户信息'); INSERT INTO `my_system`.`sys_permissions`(`perId`, `permissionsName`, `perRemarks`) VALUES ('M204', 'queryMyUserInfo', '查看个人信息');
然后是我们给小红的帐号 adminHong分配角色 admin,然后给admin这个角色分配权限菜单M01,M02,M03,M204;
然后给JC的帐号jc分批角色 common,然后给common这个角色分批权限菜单M204;
所以两张关联表的数据就是以下情况:
role_per
相关sql:
INSERT INTO `my_system`.`role_per`(`id`, `roleId`, `perId`) VALUES (1, '100', 'M01'); INSERT INTO `my_system`.`role_per`(`id`, `roleId`, `perId`) VALUES (2, '100', 'M02'); INSERT INTO `my_system`.`role_per`(`id`, `roleId`, `perId`) VALUES (3, '100', 'M03'); INSERT INTO `my_system`.`role_per`(`id`, `roleId`, `perId`) VALUES (4, '200', 'M204'); INSERT INTO `my_system`.`role_per`(`id`, `roleId`, `perId`) VALUES (5, '100', 'M204');
user_role
相关sql:
INSERT INTO `my_system`.`user_role`(`id`, `userId`, `roleId`) VALUES (1, 10001, '100'); INSERT INTO `my_system`.`user_role`(`id`, `userId`, `roleId`) VALUES (2, 20001, '200');
其实看到这些表的模拟数据,看到这些关联关系,实现个权限系统,非常简单明了。
只需要根据帐号查询出来相关的数据,接下来怎么进行权限校验不是非常轻松的事情么。
例如我们执行一个这样的sql语句:
SELECT user.userId ,user.userName,role.roleName,role.roleId,per.permissionsName ,per.perId,per.perRemarks FROM sys_user AS user, sys_role AS role, sys_permissions AS per, role_per, user_role WHERE user.userName='adminHong' AND user.userId=user_role.userId AND user_role.roleId=role.roleId AND role_per.roleId=role.roleId AND role_per.perId=per.perId
查询出来的结果是:
根据用户登录帐号查询出这个帐号的角色身份,权限,结果非常清晰。
再查下另一个帐号,也是非常清晰:
ok,这么啰嗦地终于从数据库的设计 以及 模拟帐号注册,创建角色,创建权限,给帐号分批角色,给角色分批权限生成的数据
的层面场景,大致给这套系统呈现了个漂浮的使用场景。
代码实现
接下来就是看咱们怎么结合这个数据库去实现了。
先来看看我们的最后项目结构(里面的controller就是模拟的各个功能模块,日志功能,登录功能,导出功能):
创建一个springboot项目,
然后在pom.xml里,加入我们需要使用到的jar包:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.jc</groupId> <artifactId>shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shiro</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--web项目--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- druid数据源驱动 1.1.10解决springboot从1.0——2.0版本问题--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--mysql连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--lombok简化pojo代码--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <!--shiro权限框架--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
然后是application.yml文件:
server: port: 8077 spring: datasource: druid: username: root password: root url: jdbc:mysql://localhost:3306/my_system?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 mybatis: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml
接下来是对照数据库表结构,创建三个pojo:
SysUser.java
import lombok.Data; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Data public class SysUser { private Integer userId; private String userName; private String password; private String userRemarks; }
SysRole.class
import lombok.Data; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Data public class SysRole { private String roleId; private String roleName; private String roleRemarks; }
SysPermissions.java
import lombok.Data; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Data public class SysPermissions { private Integer perId; private String permissionsName; private String perRemarks; }
实体类创建完毕,先不用管那些操作表的增删改查。
核心使用环节, 创建ShiroConfig.java :
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Configuration public class ShiroConfig { @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } //将自己的验证方式加入容器 @Bean public UserRealm myShiroRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } //权限管理,配置主要是Realm的管理认证 @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } //对url的过滤筛选 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> map = new HashMap<>(); //登出 map.put("/logout", "logout"); //对所有用户认证 map.put("/**", "authc"); //登录 shiroFilterFactoryBean.setLoginUrl("/login"); //成功登录后跳转的url //shiroFilterFactoryBean.setSuccessUrl("/xxxx"); //错误页面,认证不通过跳转 // shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
创建自定义的权限审核处理类,UserRealm.java(涉及到数据库的查询文章后面有写):
import com.jc.shiro.pojo.SysUser; import com.jc.shiro.service.LoginService; 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.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.Map; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ public class UserRealm extends AuthorizingRealm { @Autowired private LoginService loginService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录用户名 String userName = (String) principalCollection.getPrimaryPrincipal(); //添加角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); List<Map<String, Object>> powerList = loginService.getUserPower(userName); System.out.println(powerList.toString()); for (Map<String, Object> powerMap : powerList) { //添加角色 simpleAuthorizationInfo.addRole(String.valueOf(powerMap.get("roleName"))); //添加权限 simpleAuthorizationInfo.addStringPermission(String.valueOf(powerMap.get("permissionsName"))); } return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //加这一步的目的是在Post请求的时候会先进认证,然后在到请求 if (authenticationToken.getPrincipal() == null) { return null; } //获取用户信息 String userName = authenticationToken.getPrincipal().toString(); //根据用户名去数据库查询用户信息 SysUser sysUser = loginService.queryUser(userName); if (sysUser == null) { //这里返回后会报出对应异常 return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, sysUser.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } }
然后是我们的登录接口,这里融合自己项目的帐号加密方法,LoginController.java:
import com.jc.shiro.service.LoginService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Controller public class LoginController { @Autowired LoginService loginService; @ResponseBody @GetMapping("/login") public String login(@RequestParam("userName") String userName, @RequestParam("password") String password) { //添加用户认证信息 Subject subject = SecurityUtils.getSubject(); //自己系统的密码加密方式 ,这里简单示例一下MD5 String md5Password = DigestUtils.md5DigestAsHex(password.getBytes()); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName, md5Password); try { //进行验证,AuthenticationException可以catch到,但是AuthorizationException因为我们使用注解方式,是catch不到的,所以后面使用全局异常捕抓去获取 subject.login(usernamePasswordToken); } catch (AuthenticationException e) { e.printStackTrace(); return "账号或密码错误!"; } catch (AuthorizationException e) { e.printStackTrace(); return "没有权限"; } return "login success"; } }
其中用到了一个根据用户登录帐号去查询对应的角色权限信息,也就是文章开头我们设计的查询。
mapper层:
LoginMapper.java
import com.jc.shiro.pojo.SysUser; import org.apache.ibatis.annotations.Mapper; import java.util.List; import java.util.Map; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Mapper public interface LoginMapper { SysUser queryUser(String userName ); List<Map<String,Object>> getUserPower(String userName); }
loginMapper.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.jc.shiro.mapper.LoginMapper"> <!--通过登录帐号查找用户信息--> <select id="queryUser" resultType="com.jc.shiro.pojo.SysUser" parameterType="String"> SELECT * FROM sys_user WHERE userName=#{userName} </select> <!--通过登录帐号查找用户权限信息--> <select id="getUserPower" resultType="java.util.HashMap" parameterType="String"> SELECT user.userId ,user.userName,role.roleName,role.roleId,per.permissionsName ,per.perId,per.perRemarks FROM sys_user AS user, sys_role AS role, sys_permissions AS per, role_per, user_role WHERE user.userName=#{userName} AND user.userId=user_role.userId AND user_role.roleId=role.roleId AND role_per.roleId=role.roleId AND role_per.perId=per.perId </select> </mapper>
然后是service层:
LoginService.java
import com.jc.shiro.pojo.SysUser; import java.util.List; import java.util.Map; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ public interface LoginService { SysUser queryUser(String userName ); List<Map<String,Object>> getUserPower(String userName ); }
LoginServiceImpl.java
import com.jc.shiro.mapper.LoginMapper; import com.jc.shiro.pojo.SysUser; import com.jc.shiro.service.LoginService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @Service public class LoginServiceImpl implements LoginService { @Autowired LoginMapper loginMapper; @Override public SysUser queryUser(String userName) { return loginMapper.queryUser(userName); } @Override public List<Map<String, Object>> getUserPower(String userName) { return loginMapper.getUserPower(userName); } }
最后再补上一个异常全局控制器,MyExceptionHandler.java:
import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.AuthorizationException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @ControllerAdvice @Slf4j public class MyExceptionHandler { @ExceptionHandler @ResponseBody public String ErrorHandler(AuthorizationException e) { log.error("权限校验失败!", e); return "您暂时没有权限,请联系管理员!"; } }
到这里,已经整合完毕,接下来是我们的使用,后端的校验我们采取shiro提供的注解的方式去使用:
@RequiresRoles("xxx")
@RequiresPermissions("xxx")
我们简单模拟导出数据功能模块,ExportController.java:
暂时只提供了一个导出接口,而这个导出接口需要用户拥有 admin 角色 以及 exportUserInfo 权限,也就是代码里注解的参数
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @RestController public class ExportController { @RequiresRoles("admin") @RequiresPermissions("exportUserInfo") @ResponseBody @RequestMapping("/export") public String export() { return "u can export !"; } }
再来模拟日志功能模块,LogController.java:
import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @Author : JCccc * @CreateTime : 2020/4/24 * @Description : **/ @RestController public class LogController { //注解验角色和权限 @RequiresRoles("common") @ResponseBody @RequestMapping("/querySystemLog") public String queryLog() { return "u can queryLog !"; } }
好,接下来我们从登录开始,测试下整体的效果:
我们先用普通用户帐号 jc 登录看看,
登录成功后,我们去访问一下导出接口,因为导出接口, 可以看到因为jc是个普通用户,没有对应上角色和权限,所以无法正常访问:
那么访问下日志的查询接口,这个帐号是有权限的:
管理员帐号的登录测试也是跟上面一样,只要后端对接口添加的注解角色和权限对应上就可以访问,对应不上就会拦截。
快速模拟展示下,
登录:
访问导出数据接口,成功访问:
访问查询日志接口,没有权限,访问失败:
最后,其实管理员怎么能没有查询日志的权限呢? 数据库里,admin角色和普通用户角色都是拥有这个权限的:
原因出在我们在查询日志的接口上,配置的角色身份是:
那么我们想让管理员也能访问,我们不需要对管理员小红帐号再分配个普通用户的角色,
而是改动下接口的注解的使用,这两个shiro提供的注解传入的并不是单一参,是可以接收多个的:
下面的Logical 的 默认值是Logical.AND,是指这个接口的访问用户必须同时具备所有角色才可以访问,我们这边是想实现管理员或者普通用户都能访问。
所以对于这些多对个角色都能拥有的功能,只要数据库里的分配了对应的权限,我们在接口上使用注解传入对应的参数即可:
@RequiresRoles(value={"admin","common"},logical= Logical.OR)
ps:这样这个接口就是这个用户在数据库里面,只要角色是admin或者common都能访问。
重新启动项目,登录管理员小红的帐号adminHong,访问接口查询日志,可以看到能成功访问:
最后再对这两个注解说明一下,这两个角色校验注解和权限码校验注解,
可以单一的使用,也可以同时的使用,也可以不适用。根据业务场景,哪些接口只需要用户拥有角色即可,哪些接口需要具体到权限码,这些都是自己去做调整。
我个人建议的是,每个接口都具体到角色+权限码,这样比较严谨统一点,也不会乱套。
整合shiro框架的使用,除了它自带的那套密码验证流程外,对与权限,角色的分配和校验,核心就是 我们根据业务自定义的
UserRealm.java ,想更熟悉的伙伴,只需要在登录接口打断点,在UserRealm打断点,然后仿照我的测试流程,从登录到接口访问地去调接口,就可以更清晰地了解了。