对于一个后台管理系统,权限访问控制非常重要,而实现权限控制的方法有很多
- 过滤器
- 拦截器
- AOP
- 微服务网关
这篇文章主要介绍如何通过aop整合shiro权限框架来实现权限访问控制。
第一步:准备数据库
实现权限管理一般需要5张表:用户表、角色表、权限表、用户-角色关系表、角色-权限关系表。
1、管理员用户表
-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '12345' COMMENT '密码',
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号',
`gender` tinyint UNSIGNED NOT NULL COMMENT '性别',
`is_enable` tinyint NOT NULL,
`last_login_time` datetime NULL DEFAULT NULL COMMENT '上一次登录时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '管理员表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of admin
-- ----------------------------
INSERT INTO `admin` VALUES ('2023', '系统管理员', 'system', '12345', '17777777777', 2, 1, '2022-11-18 10:45:57');
INSERT INTO `admin` VALUES ('mhxy1218', '沐雨橙风ιε', 'mumu', 'mhxy1218', '16782209635', 1, 1, '2022-12-31 19:38:43');
2、角色表
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '名称',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '描述',
`sort` int UNSIGNED NULL DEFAULT 0 COMMENT '自定义排序序号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统角色表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员', '最高权限,拥有系统所有权限', 0);
INSERT INTO `role` VALUES (2, '系统管理员', '拥有系统设置相关权限', 100);
3、权限表
权限表的数据通过扫描controller接口信息得到,文章后面会介绍怎么获取。
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
`value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '值',
`type` tinyint UNSIGNED NOT NULL COMMENT '权限类型(目录/菜单)',
`parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '父级权限id',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '接口路径',
`method` tinyint UNSIGNED NULL DEFAULT 0 COMMENT '请求方式(0-get;1-post)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统权限表' ROW_FORMAT = DYNAMIC;
4、用户-角色表
-- ----------------------------
-- Table structure for admin_role
-- ----------------------------
DROP TABLE IF EXISTS `admin_role`;
CREATE TABLE `admin_role` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`admin_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '管理员id',
`role_id` int UNSIGNED NOT NULL COMMENT '角色id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '管理员-角色关系表表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of admin_role
-- ----------------------------
INSERT INTO `admin_role` VALUES (1, 'mhxy1218', 1);
INSERT INTO `admin_role` VALUES (2, '2023', 2);
5、角色-权限表
-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`role_id` int UNSIGNED NOT NULL COMMENT '角色id',
`permission_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1281 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色-权限关联表' ROW_FORMAT = DYNAMIC;
二:shiro的相关配置
1、pom.xml中添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
项目完整配置文件如下:
<?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.3.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>cn.edu.sgu.www</groupId>
<artifactId>mhxysy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<mysql.version>8.0.28</mysql.version>
<druid.version>1.1.21</druid.version>
<lombok.version>1.18.22</lombok.version>
<mybatis-boot.version>2.2.2</mybatis-boot.version>
<mybatis-plus.version>3.5.1</mybatis-plus.version>
<shiro.version>1.3.2</shiro.version>
<fastjson.version>2.0.8</fastjson.version>
<knife4j.version>2.0.9</knife4j.version>
<flyway.version>5.2.1</flyway.version>
<easyexcell.version>2.1.1</easyexcell.version>
<nacos.version>2.2.0.RELEASE</nacos.version>
<feign.version>2.2.9.RELEASE</feign.version>
<sentinel.version>2.2.9.RELEASE</sentinel.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-test</artifactId>
<scope>test</scope>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-boot.version}</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--flyway-->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!--easy-excel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcell.version}</version>
</dependency>
<!--nacos注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
</dependency>
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${nacos.version}</version>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${feign.version}</version>
</dependency>
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${sentinel.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、创建Redis工具类
RedisUtils.java
package cn.edu.sgu.www.mhxysy.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class RedisUtils {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public RedisUtils(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 保存数据到Redis
* @param key 键
* @param value 值
*/
public void save(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
redisTemplate.expire(key, 7, TimeUnit.DAYS);
}
/**
* 通过用户名获取用户的权限信息
* @param key 键
*/
public Object get(String key) {
log.debug("从redis中获取key{}...", key);
if (hasKey(key)) {
return redisTemplate.opsForValue().get(key);
} else {
return null;
}
}
/**
* 通过用户名删除用户的权限信息
* @param key 用户名
*/
public void remove(String key) {
if (hasKey(key)) {
redisTemplate.delete(key);
log.debug("从redis中删除key{}...", key);
}
}
/**
* 删除全部用户的权限信息
*/
public void removeAll() {
Set<String> keys = redisTemplate.keys("*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
log.debug("从redis中删除全部key...");
}
/**
* 判断key是否存在
* @param key 键
* @return Boolean
*/
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
}
RedisRepository.java:只用于保存用户权限信息
package cn.edu.sgu.www.mhxysy.redis;
import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixConst;
import cn.edu.sgu.www.mhxysy.feign.FeignService;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class RedisRepository extends RedisUtils {
private static final String SUFFIX = RedisKeyPrefixConst.PREFIX_ROLE_PERMISSION;
private final FeignService feignService;
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public RedisRepository(FeignService feignService, RedisTemplate<String, Object> redisTemplate) {
super(redisTemplate);
this.feignService = feignService;
this.redisTemplate = redisTemplate;
}
/**
* 查询用户的权限信息,并保存到redis
* @param username 用户名
*/
public void save(String username) {
// 查询用户的权限
List<String> permissions = feignService.selectPermissionsByUsername(username);
// 拼接得到key
String key = SUFFIX + username;
redisTemplate.opsForValue().set(key, JSON.toJSONString(permissions));
redisTemplate.expire(key, 7, TimeUnit.DAYS);
log.debug("用户{}的权限保存到了redis中", username);
}
/**
* 通过用户名获取用户的权限信息
* @param username 用户名
*/
@Override
public List<String> get(String username) {
String key = SUFFIX + username;
log.info("从redis中获取用户{}的权限", username);
if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
String permissions = (String) redisTemplate.opsForValue().get(key);
if (StringUtils.isNotEmpty(permissions)) {
Object object = JSON.parse(permissions);
return object != null ? (List<String>) object : null;
}
return null;
} else {
save(username);
return get(username);
}
}
/**
* 通过用户名删除用户的权限信息
* @param username 用户名
*/
@Override
public void remove(String username) {
String key = SUFFIX + username;
super.remove(key);
log.debug("从redis中删除用户{}的权限...", username);
}
}
3、创建Redis配置类
RedisConfig.java
package cn.edu.sgu.www.mhxysy.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.util.List;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, List<String>> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, List<String>> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.json()); // 值(Value)使用JSON
return redisTemplate;
}
}
三、创建一个Realm
AdminRealm.java
package cn.edu.sgu.www.mhxysy.realm;
import cn.edu.sgu.www.mhxysy.entity.system.Admin;
import cn.edu.sgu.www.mhxysy.redis.RedisRepository;
import cn.edu.sgu.www.mhxysy.service.system.AdminService;
import org.apache.shiro.authc.*;
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.HashSet;
import java.util.List;
import java.util.Set;
public class AdminRealm extends AuthorizingRealm {
@Autowired
private AdminService adminService;
@Autowired
private RedisRepository redisRepository;
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
// 得到令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 从token中获取用户的登录信息
String username = token.getUsername();
String password = new String(token.getPassword());
// 根据用户名查询管理员信息
Admin admin = adminService.selectByUsername(username);
if (admin != null && admin.getPassword().equals(password)) {
return new SimpleAuthenticationInfo(admin, password, username);
}
return null;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
Admin admin = (Admin) principals.getPrimaryPrincipal();
String username = admin.getUsername();
// 从redis中获取用户的权限
List<String> permissions = redisRepository.get(username);
Set<String> set = new HashSet<>(permissions);
authorizationInfo.setStringPermissions(set);
return authorizationInfo;
}
}
四、创建shiro配置类
ShiroConfig.java
package cn.edu.sgu.www.mhxysy.config;
import cn.edu.sgu.www.mhxysy.realm.AdminRealm;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
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 java.util.LinkedHashMap;
import java.util.Map;
/**
* shiro配置类
*/
@Configuration
public class ShiroConfig {
@Bean
public AdminRealm adminRealm() {
return new AdminRealm();
}
/**
* 配置安全管理器
* @param adminRealm 管理员realm
* @return DefaultWebSecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("adminRealm") AdminRealm adminRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(adminRealm);
return securityManager;
}
/**
* 配置Shiro过滤器工厂
* @param securityManager 安全管理器
* @return ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 注册安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 当用户访问认证资源的时候,如果用户没有登录,那么就会跳转到该属性指定的页面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 定义资源访问规则
Map<String, String> map = new LinkedHashMap<>();
map.put("/index.html", "authc");
map.put("/html/*", "authc");
map.put("/", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
五、创建AOP类
创建一个鉴权aop类AuthenticationAop.java,在每个控制器接口前进行鉴权操作
package cn.edu.sgu.www.mhxysy.aop;
import cn.edu.sgu.www.mhxysy.annotation.AnonymityAccess;
import cn.edu.sgu.www.mhxysy.config.NetAccessConfig;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.util.IpUtils;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 鉴权AOP类
*/
@Slf4j
@Aspect
@Component
public class AuthenticationAop {
/**
* 3秒内最多处理10次请求
*/
@Value("${access.count.three-seconds}")
private int times;
/**
* 3秒内最多处理10次请求
*/
@Value("${access.limit}")
private boolean limit;
private final NetAccessConfig netAccessConfig;
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public AuthenticationAop(NetAccessConfig netAccessConfig, RedisTemplate<String, Object> redisTemplate) {
this.netAccessConfig = netAccessConfig;
this.redisTemplate = redisTemplate;
}
@Pointcut("execution(public * cn.edu.sgu.www.mhxysy.controller..*(..))")
public void requestAspect(){}
@Before(value = "requestAspect()")
public void before(JoinPoint joinPoint) throws Throwable {
// 是否开启内外网访问控制
boolean innerIpAccess = netAccessConfig.isEnableInnerIpAccess();
// 获取配置的内网IP地址
List<String> innerIps = netAccessConfig.getInnerIps();
// 获取客户端IP
String ip = IpUtils.getIp();
// 判断是不是内网访问
if (innerIpAccess && !innerIps.contains(ip)) {
throw new GlobalException(ResponseCode.FORBIDDEN, "只允许内网访问~");
}
synchronized (AuthenticationAop.class) {
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~进入方法AuthenticationAop.before()");
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
HttpServletRequest request = UserUtils.getRequest();
String requestURI = request.getRequestURI();
//打印请求信息
log.debug("COOKIE:{}", request.getHeader("COOKIE"));
log.info("请求ip:{}", request.getRemoteAddr());
log.info("请求地址:{}", requestURI);
log.info("请求方式:{}", request.getMethod());
log.info("请求类方法:{}", joinPoint.getSignature());
log.info("请求类方法参数:{}", Arrays.toString(joinPoint.getArgs()));
if (limit) {
// 3秒内最多处理10次请求
Long count = redisTemplate.boundValueOps(requestURI).increment();
if(count != null) {
if (count.intValue() == 1) {
redisTemplate.expire(requestURI, 3, TimeUnit.SECONDS);
} else if (count.intValue() > times) {
throw new GlobalException(ResponseCode.FORBIDDEN, "服务器忙...");
}
}
}
// 开启了匿名访问,放行
if (method.isAnnotationPresent(AnonymityAccess.class)) {
AnonymityAccess annotation = method.getAnnotation(AnonymityAccess.class);
if (annotation.value()) {
log.debug("接口{}开启了匿名访问,放行", requestURI);
return;
}
}
handlePermissions(method);
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.before()执行完成");
}
}
@After(value = "requestAspect()")
public void after() throws Throwable {
HttpServletResponse response = UserUtils.getResponse();
Collection<String> headerNames = response.getHeaderNames();
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()开始执行");
for (String headerName : headerNames) {
String header = response.getHeader(headerName);
log.debug("header => {}:{}", headerName, header);
}
log.debug("~~~~~~~~~~~~~~~~~~~~~~~~~~AuthenticationAop.after()执行完成");
}
/**
* 判断用户是否有访问资源需要的权限
* @param method Method
*/
@Deprecated
private void handlePermissions(Method method) {
log.debug("开始鉴权:~~~");
// 鉴权流程开始
Class<?> cls = method.getDeclaringClass();
if (cls.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);
// 获取控制器类的路径,如:/chongwu
String[] path = requestMapping.path();
if (path.length == 0) {
path = requestMapping.value();
}
String prefix = path[0];
if (method.isAnnotationPresent(RequestMapping.class)) {
requestMapping = method.getAnnotation(RequestMapping.class);
// 获取控制器类方法上的路径,如:/selectByPage
String[] value = requestMapping.value();
if (value.length == 0) {
value = requestMapping.path();
}
// 拼接得到接口的完整路径,如:/chongwu/selectByPage
String requestUrl = prefix + value[0];
// 调用shiro鉴权,判断用户是否有权限访问改请求的路径
boolean result = UserUtils.getSubject().isPermitted(requestUrl);
// 无权访问,抛出异常返回
if (!result) {
log.error("正在访问未授权的资源:{}", requestUrl);
throw new GlobalException(ResponseCode.UNAUTHORIZED, "您正在访问未授权的资源。");
}
}
}
}
}
六、创建对应的实体类
1、Admin.java
package cn.edu.sgu.www.mhxysy.entity.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 管理员
* @author heyunlin
* @version 1.0
*/
@Data
@TableName("admin")
public class Admin implements Serializable {
private static final long serialVersionUID = 18L;
@TableId(value = "id", type = IdType.INPUT)
private String id;
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private Integer gender;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 手机号
*/
private String phone;
/**
* 是否启用
*/
private Integer isEnable;
/**
* 最后一次登录时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime lastLoginTime;
}
2、AdminRole.java
package cn.edu.sgu.www.mhxysy.entity.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 管理员登录历史
* @author heyunlin
* @version 1.0
*/
@Data
@TableName("admin_role")
public class AdminRole implements Serializable {
private static final long serialVersionUID = 18L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 管理员id
*/
private String adminId;
/**
* 角色ID
*/
private Integer roleId;
}
3、Permission.java
package cn.edu.sgu.www.mhxysy.entity.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 权限
* @author heyunlin
* @version 1.0
*/
@Data
@TableName("permission")
public class Permission implements Serializable {
private static final long serialVersionUID = 18L;
@TableId(value = "id", type = IdType.AUTO)
private String id;
/**
* 权限名称
*/
private String name;
/**
* 类型
* 0-顶级权限(控制器类)
* 1-子级权限(接口方法)
*/
private Integer type;
/**
* 请求路径
*/
private String url;
/**
* 请求方式:0-get;1-post
*/
private Integer method;
/**
* 服务名
*/
private String service;
/**
* 父级权限id
*/
private String parentId;
}
4、Role.java
package cn.edu.sgu.www.mhxysy.entity.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 角色
* @author heyunlin
* @version 1.0
*/
@Data
@TableName("role")
public class Role implements Serializable {
private static final long serialVersionUID = 18L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 角色名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
}
5、RolePermission.java
package cn.edu.sgu.www.mhxysy.entity.system;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* 角色权限
* @author heyunlin
* @version 1.0
*/
@Data
@TableName("role_permission")
public class RolePermission implements Serializable {
private static final long serialVersionUID = 18L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 角色id
*/
private Integer roleId;
/**
* 权限id
*/
private String permissionId;
}
七、持久层
持久层使用了mybatis-plus,直接给出代码
1、AdminMapper.java
package cn.edu.sgu.www.mhxysy.mapper.system;
import cn.edu.sgu.www.mhxysy.entity.system.Admin;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AdminMapper extends BaseMapper<Admin> {
}
AdminMapper.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="cn.edu.sgu.www.mhxysy.mapper.system.AdminMapper">
<resultMap id="resultMap" type="cn.edu.sgu.www.mhxysy.vo.TableInfoVO">
<result property="name" column="table_name" />
<result property="comment" column="table_comment" />
</resultMap>
<select id="getTableNames" resultType="java.lang.String">
select table_name from table_names
</select>
<select id="selectTables" resultMap="resultMap">
select table_name, table_comment from table_names
</select>
</mapper>
2、AdminRoleMapper.java
package cn.edu.sgu.www.mhxysy.mapper.system;
import cn.edu.sgu.www.mhxysy.entity.system.AdminRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface AdminRoleMapper extends BaseMapper<AdminRole> {
}
AdminRoleMapper.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="cn.edu.sgu.www.mhxysy.mapper.system.AdminRoleMapper">
</mapper>
3、PermissionMapper.java
package cn.edu.sgu.www.mhxysy.mapper.system;
import cn.edu.sgu.www.mhxysy.entity.system.Permission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface PermissionMapper extends BaseMapper<Permission> {
/**
* 通过服务名删除权限
* @param service 服务名
*/
void deleteByService(String service);
/**
* 通过类型查询权限列表
* @return List<Permission>
*/
List<Permission> selectByType(Integer type);
/**
* 通过父级权限id查询权限列表
* @param parentId 父级权限id
* @return List<Permission>
*/
List<Permission> selectByParentId(String parentId);
/**
* 通过用户名查询权限
* @param username 用户名
* @return Set<String>
*/
List<String> selectPermissionsByUsername(String username);
}
PermissionMapper.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="cn.edu.sgu.www.mhxysy.mapper.system.PermissionMapper">
<resultMap id="baseMap" type="cn.edu.sgu.www.mhxysy.entity.system.Permission">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="url" property="url" />
<result column="type" property="type" />
<result column="method" property="method" />
<result column="parent_id" property="parentId" />
</resultMap>
<delete id="deleteByService">
delete from permission where service = #{service}
</delete>
<select id="selectByType" resultMap="baseMap">
select * from permission where type = #{type}
</select>
<select id="selectByParentId" resultMap="baseMap">
select * from permission where parent_id = #{parentId}
</select>
<select id="selectPermissionsByUsername" resultType="java.lang.String">
select p.url from permission p
left join role_permission rp on p.id = rp.permission_id
left join admin_role ar on rp.role_id = ar.role_id
left join admin adm on ar.admin_id = adm.id
where adm.username= #{username}
</select>
</mapper>
4、RoleMapper.java
package cn.edu.sgu.www.mhxysy.mapper.system;
import cn.edu.sgu.www.mhxysy.entity.system.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleMapper extends BaseMapper<Role> {
}
RoleMapper.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="cn.edu.sgu.www.mhxysy.mapper.system.RoleMapper">
</mapper>
5、RolePermissionMapper.java
package cn.edu.sgu.www.mhxysy.mapper.system;
import cn.edu.sgu.www.mhxysy.entity.system.Permission;
import cn.edu.sgu.www.mhxysy.entity.system.RolePermission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RolePermissionMapper extends BaseMapper<RolePermission> {
/**
* 清空权限列表
*/
void clearPermissions();
/**
* 通过角色id查询
* @param roleId 角色id
* @return List<Permission>
*/
List<Permission> selectByRoleId(Integer roleId);
}
<?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="cn.edu.sgu.www.mhxysy.mapper.system.RolePermissionMapper">
<resultMap id="baseMap" type="cn.edu.sgu.www.mhxysy.entity.system.Permission">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="url" property="url" />
<result column="type" property="type" />
<result column="method" property="method" />
<result column="service" property="service" />
<result column="parent_id" property="parentId" />
</resultMap>
<update id="clearPermissions">
truncate table role_permission
</update>
<select id="selectByRoleId" resultMap="baseMap">
select p.id, p.name, p.type, p.method, p.url, p.service, p.parent_id from permission p
left join role_permission rp on p.id = rp.permission_id
where rp.role_id = #{roleId}
</select>
</mapper>
八、service层
1、AdminService.java
package cn.edu.sgu.www.mhxysy.service.system;
import cn.edu.sgu.www.mhxysy.dto.system.AdminLoginDTO;
import cn.edu.sgu.www.mhxysy.entity.system.Admin;
import cn.edu.sgu.www.mhxysy.pager.system.AdminPager;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
public interface AdminService {
/**
* 添加管理员
* @param admin 管理员信息
*/
void insert(Admin admin);
/**
* 通过id删除管理员
* @param id 管理员id
*/
@Transactional
void deleteById(String id);
/**
* 根据id修改管理员信息
* @param admin 管理员信息
*/
void updateById(Admin admin);
/**
* 查询全部管理员
* @return List<Admin>
*/
List<Admin> selectAll();
/**
* 通过id查询管理员信息
* @param id 管理员id
* @return Admin
*/
Admin selectById(String id);
/**
* 根据用户名查询管理员信息
* @param username 用户名
* @return Admin 通过用户名查询到的查询结果
*/
Admin selectByUsername(String username);
/**
* 查询数据库所有表的名称
* @return List<String>
*/
List<String> getTableNames();
/**
* 分页查询管理员列表
* @param pager 分页参数
* @return Page<Admin>
*/
Page<Admin> selectByPage(AdminPager pager);
/**
* 管理员登录
* @param adminLoginDTO 登录信息
*/
@Transactional(rollbackFor = Exception.class)
void login(AdminLoginDTO adminLoginDTO);
/**
* 退出登录
*/
void logout();
}
AdminServiceImpl.java
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.base.Pager;
import cn.edu.sgu.www.mhxysy.dto.system.AdminLoginDTO;
import cn.edu.sgu.www.mhxysy.entity.system.Admin;
import cn.edu.sgu.www.mhxysy.entity.system.AdminLoginLog;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.mapper.system.AdminLoginLogMapper;
import cn.edu.sgu.www.mhxysy.mapper.system.AdminMapper;
import cn.edu.sgu.www.mhxysy.pager.system.AdminPager;
import cn.edu.sgu.www.mhxysy.redis.RedisRepository;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.service.system.AdminService;
import cn.edu.sgu.www.mhxysy.util.IpUtils;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author heyunlin
* @version 1.0
*/
@Slf4j
@Service
public class AdminServiceImpl implements AdminService {
private final AdminMapper mapper;
private final AdminLoginLogMapper loginLogMapper;
private final RedisRepository redisRepository;
@Value("${syslog.enable}")
private boolean enable;
@Autowired
public AdminServiceImpl(AdminMapper mapper, AdminLoginLogMapper loginLogMapper, RedisRepository redisRepository) {
this.mapper = mapper;
this.loginLogMapper = loginLogMapper;
this.redisRepository = redisRepository;
}
@Override
public void insert(Admin admin) {
admin.setId(StringUtils.uuid());
mapper.insert(admin);
}
@Override
public void deleteById(String id) {
// 删除管理员登录日志
QueryWrapper<AdminLoginLog> wrapper = new QueryWrapper<>();
wrapper.eq("admin_id", id);
loginLogMapper.delete(wrapper);
// 删除管理员
mapper.deleteById(id);
}
@Override
public void updateById(Admin admin) {
mapper.updateById(admin);
}
@Override
public List<Admin> selectAll() {
return mapper.selectList(null);
}
@Override
public Admin selectById(String id) {
return mapper.selectById(id);
}
@Override
public Admin selectByUsername(String username) {
QueryWrapper<Admin> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
List<Admin> list = mapper.selectList(wrapper);
if (list.isEmpty()) {
throw new GlobalException(ResponseCode.NOT_FOUND, "查询失败,管理员不存在");
} else if (list.size() > 1) {
throw new GlobalException(ResponseCode.CONFLICT, "数据异常,查询到多条管理员信息");
} else {
return list.get(0);
}
}
@Override
public List<String> getTableNames() {
return mapper.getTableNames();
}
@Override
public Page<Admin> selectByPage(AdminPager pager) {
QueryWrapper<Admin> wrapper = new QueryWrapper<>();
Page<Admin> page = Pager.ofPage(pager);
wrapper.eq(
StringUtils.isNotEmpty(pager.getPhone()),
"phone", pager.getPhone()
);
return mapper.selectPage(page, wrapper);
}
@Override
public void login(AdminLoginDTO loginDTO) {
// 得到用户名
String username = loginDTO.getUsername();
log.trace("用户{}正在登录...", username);
// shiro登录认证
UsernamePasswordToken token = new UsernamePasswordToken(username, loginDTO.getPassword());
Subject subject = UserUtils.getSubject();
subject.login(token);
// 设置session失效时间:永不超时
subject.getSession().setTimeout(-1001);
// 修改管理员上一次登录时间
Admin admin = selectByUsername(username);
admin.setLastLoginTime(LocalDateTime.now());
mapper.updateById(admin);
// 如果开启了系统日志
if (enable) {
// 添加管理员登录历史
AdminLoginLog history = new AdminLoginLog();
history.setId(StringUtils.uuid());
history.setAdminId(admin.getId());
history.setLoginTime(LocalDateTime.now());
history.setLoginIp(IpUtils.getLocalHostAddress());
history.setLoginHostName(IpUtils.getLocalHostName());
loginLogMapper.insert(history);
}
// 从redis中删除用户权限
redisRepository.remove(username);
// 查询用户的权限信息,并保存到redis
redisRepository.save(username);
}
@Override
public void logout() {
// 删除角色的权限
redisRepository.remove(UserUtils.getLoginUsername());
// 注销
UserUtils.getSubject().logout();
}
}
2、AdminRoleService.java
package cn.edu.sgu.www.mhxysy.service.system;
import cn.edu.sgu.www.mhxysy.entity.system.AdminRole;
import cn.edu.sgu.www.mhxysy.pager.system.AdminRolePager;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* @author heyunlin
* @version 1.0
*/
public interface AdminRoleService {
/**
* 添加管理员角色
* @param role 管理员角色信息
*/
void insert(AdminRole role);
/**
* 通过id删除管理员角色
* @param id 管理员角色id
*/
void deleteById(Integer id);
/**
* 通过id修改管理员角色信息
* @param role 管理员角色信息
*/
void updateById(AdminRole role);
/**
* 分页查询管理员角色列表
* @param pager 分页查询的条件
* @return Page<AdminRole>
*/
Page<AdminRole> selectByPage(AdminRolePager pager);
}
AdminRoleServiceImpl.java
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.base.Pager;
import cn.edu.sgu.www.mhxysy.entity.system.AdminRole;
import cn.edu.sgu.www.mhxysy.mapper.system.AdminRoleMapper;
import cn.edu.sgu.www.mhxysy.pager.system.AdminRolePager;
import cn.edu.sgu.www.mhxysy.service.system.AdminRoleService;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author heyunlin
* @version 1.0
*/
@Service
public class AdminRoleServiceImpl implements AdminRoleService {
private final AdminRoleMapper mapper;
@Autowired
public AdminRoleServiceImpl(AdminRoleMapper mapper) {
this.mapper = mapper;
}
@Override
public void insert(AdminRole role) {
mapper.insert(role);
}
@Override
public void deleteById(Integer id) {
mapper.deleteById(id);
}
@Override
public void updateById(AdminRole role) {
mapper.updateById(role);
}
@Override
public Page<AdminRole> selectByPage(AdminRolePager pager) {
QueryWrapper<AdminRole> wrapper = new QueryWrapper<>();
Page<AdminRole> page = Pager.ofPage(pager);
wrapper.eq(
StringUtils.isNotEmpty(pager.getAdminId()),
"admin_id", pager.getAdminId()
);
wrapper.eq(
pager.getRoleId() != null,
"role_id", pager.getRoleId()
);
return mapper.selectPage(page, wrapper);
}
}
3、PermissionService.java
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.service.BaseService;
import cn.edu.sgu.www.common.util.StringUtils;
import cn.edu.sgu.www.mhxysy.MhxysyApplication;
import cn.edu.sgu.www.mhxysy.dto.pager.PermissionPager;
import cn.edu.sgu.www.mhxysy.entity.system.Permission;
import cn.edu.sgu.www.mhxysy.enums.RequestMethod;
import cn.edu.sgu.www.mhxysy.mapper.system.PermissionMapper;
import cn.edu.sgu.www.mhxysy.service.system.PermissionService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@Service
public class PermissionServiceImpl extends BaseService<Permission> implements PermissionService {
private final PermissionMapper mapper;
private final static String serviceId = "mhxysy";
List<String> classPaths = new ArrayList<>();
List<Permission> resources = new ArrayList<>();
public PermissionServiceImpl(PermissionMapper mapper) {
super(mapper);
this.mapper = mapper;
}
@Override
public boolean insert(Permission permission) {
permission.setId(StringUtils.uuid());
return super.insert(permission);
}
@Override
public boolean deleteById(String id) {
super.deleteById(id);
return true;
}
@Override
public boolean updateById(Permission permission) {
super.updateById(permission);
return true;
}
@Override
public List<Permission> selectAll() {
return super.selectAll();
}
@Override
public Permission selectById(String id) {
return mapper.selectById(id);
}
@Override
public List<Permission> selectByType(Integer type) {
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.eq("type", type);
return mapper.selectList(wrapper);
}
@Override
public List<String> selectPermissionsByUsername(String username) {
return mapper.selectPermissionsByUsername(username);
}
@Override
public Page<Permission> selectByPage(PermissionPager pagerDTO) {
Page<Permission> page = new Page<>(pagerDTO.getPage(), pagerDTO.getRows());
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.eq(pagerDTO.getType() != null, "type", pagerDTO.getType());
wrapper.like(StringUtils.isNotEmpty(pagerDTO.getName()), "name", pagerDTO.getName());
wrapper.like(StringUtils.isNotEmpty(pagerDTO.getValue()), "value", pagerDTO.getValue());
wrapper.eq(StringUtils.isNotEmpty(pagerDTO.getParentId()), "parent_id", pagerDTO.getParentId());
return mapper.selectPage(page, wrapper);
}
@Override
public void init() throws ClassNotFoundException {
mapper.truncate();
String basePackage = "cn.edu.sgu.www.mhxysy.controller";
String classpath = MhxysyApplication.class.getResource("/").getPath().replaceFirst("/", "");
String searchPath = classpath + basePackage.replace(".", "/");
searchPath = searchPath.replace("test-classes", "classes");
List<String> classPaths = doPath(new File(searchPath));
for(String classPath : classPaths) {
classPath = classPath.replace(classpath.replace("/", "\\")
.replaceFirst("\\\\", ""), "")
.replace("\\", ".")
.replace(".class", "");
classpath = classPath.substring(classPath.indexOf(basePackage));
Class<?> cls = Class.forName(classpath);
RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);
// 判断多种注解的情况
String prefix = "";
Permission parent = new Permission();
if(requestMapping != null) {
// path或者value
prefix = requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0];
parent.setId(serviceId + "_" + cls.getSimpleName());
parent.setValue(prefix.substring(1));
parent.setType(0);
parent.setUrl(prefix);
// 设置value
if (cls.isAnnotationPresent(Api.class)) {
Api api = cls.getAnnotation(Api.class);
if (api != null) {
String name = api.value();
// 类的接口文档@Api注解的值以“控制器”或“控制器类”结束
parent.setName(name.substring(0, name.indexOf("控制器")).concat("管理"));
}
}
resources.add(parent);
}
Method[] methods = cls.getDeclaredMethods();
for (Method value : methods) {
getClassAnnotation(value, prefix, cls, parent.getId());
}
}
for (Permission permission : resources) {
if (permission.getName() != null) {
mapper.insert(permission);
}
}
}
/**
* 得到类上面的注解信息
* @param method
* @param prefix
* @param cls
* @param parentId
*/
public void getClassAnnotation(Method method, String prefix, Class<?> cls, String parentId) {
String url = null;
Permission permission = new Permission();
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
url = prefix + (requestMapping.value().length > 0 ? requestMapping.value()[0] : requestMapping.path()[0]);
String requestMethod = requestMapping.method().length > 0 ? requestMapping.method()[0].name() : "get";
permission.setMethod(RequestMethod.getValueByName(requestMethod));
} else if (method.isAnnotationPresent(GetMapping.class)) {
GetMapping getMapping = method.getAnnotation(GetMapping.class);
url = prefix + getMapping.value()[0];
permission.setMethod(RequestMethod.GET.getValue());
} else if (method.isAnnotationPresent(PostMapping.class)) {
PostMapping postMapping = method.getAnnotation(PostMapping.class);
url = prefix + postMapping.value()[0];
permission.setMethod(RequestMethod.POST.getValue());
}
// 处理URL
if(url != null && url.endsWith("/")) {
url = url.substring(0, url.length() - 1);
}
permission.setUrl(url);
// 设置value
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation operation = method.getAnnotation(ApiOperation.class);
if (operation != null) {
String name = operation.value();
permission.setName(name);
}
}
permission.setType(1);
permission.setParentId(parentId);
permission.setId(serviceId + "_" + cls.getSimpleName() + "_" + method.getName());
if (url != null) {
permission.setValue(String.join(">>", url.substring(1).split("/")));
}
resources.add(permission);
}
private List<String> doPath(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f1 : files) {
doPath(f1);
}
}
} else {
if (file.getName().endsWith(".class")) {
classPaths.add(file.getPath());
}
}
return classPaths;
}
}
4、RolePermissionServiceImpl
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.service.BaseService;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.common.restful.ResponseCode;
import cn.edu.sgu.www.common.util.StringUtils;
import cn.edu.sgu.www.mhxysy.dto.pager.RolePermissionPager;
import cn.edu.sgu.www.mhxysy.entity.system.Permission;
import cn.edu.sgu.www.mhxysy.entity.system.RolePermission;
import cn.edu.sgu.www.mhxysy.enums.PermissionType;
import cn.edu.sgu.www.mhxysy.mapper.system.PermissionMapper;
import cn.edu.sgu.www.mhxysy.mapper.system.RolePermissionMapper;
import cn.edu.sgu.www.mhxysy.service.system.RolePermissionService;
import cn.edu.sgu.www.mhxysy.vo.PermissionTreeChildrenVO;
import cn.edu.sgu.www.mhxysy.vo.PermissionTreeVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class RolePermissionServiceImpl extends BaseService<RolePermission> implements RolePermissionService {
private final RolePermissionMapper mapper;
private final PermissionMapper permissionMapper;
public RolePermissionServiceImpl(RolePermissionMapper mapper, PermissionMapper permissionMapper) {
super(mapper);
this.mapper = mapper;
this.permissionMapper = permissionMapper;
}
@Override
public boolean insert(RolePermission permission) {
String permissionId = permission.getPermissionId();
// 查询权限信息
Permission perm = permissionMapper.selectById(permissionId);
// 如果是父级权限,为角色添加该权限下的所有子权限
if (perm.getType().equals(PermissionType.FQX.getValue())) {
// 查询权限的所有子权限
List<Permission> permissions = permissionMapper.selectByParentId(permissionId);
// 查询角色已有权限
List<Permission> perms = mapper.selectByRoleId(permission.getRoleId());
if (perms.isEmpty()) {
permissions.forEach(p -> {
permission.setId(null);
permission.setPermissionId(p.getId());
mapper.insert(permission);
});
} else if (!permissions.containsAll(perms)) {
permissions.forEach(p -> {
if (!perms.contains(p)) {
permission.setId(null);
permission.setPermissionId(p.getId());
mapper.insert(permission);
}
});
} else {
throw new GlobalException(ResponseCode.CONFLICT, "角色已经添加了此权限。");
}
return true;
} else {
permission.setId(null);
return super.insert(permission);
}
}
@Override
public boolean deleteById(Integer id) {
return super.deleteById(id);
}
@Override
public boolean updateById(RolePermission permission) {
return super.updateById(permission);
}
@Override
public RolePermission selectById(String id) {
return mapper.selectById(id);
}
@Override
public List<PermissionTreeVO> listTree(Integer roleId) {
Map<String, List<PermissionTreeChildrenVO>> allMap = new HashMap<>();
Map<String, List<PermissionTreeChildrenVO>> map = new HashMap<>();
List<PermissionTreeVO> tree = new ArrayList<>();
// 查询全部二级权限
List<Permission> list = permissionMapper.selectByType(PermissionType.ZQX.getValue());
// 遍历,把查询出来的权限按照parentId存到map中
for (Permission permission : list) {
String parentId = permission.getParentId();
PermissionTreeChildrenVO childrenVO = new PermissionTreeChildrenVO();
childrenVO.setId(permission.getId());
childrenVO.setText(permission.getName());
childrenVO.setChecked(true);
if (allMap.containsKey(parentId)) {
allMap.get(parentId).add(childrenVO);
} else {
allMap.put(parentId, new ArrayList<>());
}
}
// 查询当前用户的角色权限
List<Permission> permissions = mapper.selectByRoleId(roleId);
// 遍历,把查询出来的权限按照parentId存到map中
for (Permission permission : permissions) {
String parentId = permission.getParentId();
PermissionTreeChildrenVO childrenVO = new PermissionTreeChildrenVO();
childrenVO.setId(permission.getId());
childrenVO.setText(permission.getName());
childrenVO.setChecked(true);
if (map.containsKey(parentId)) {
map.get(parentId).add(childrenVO);
} else {
map.put(parentId, new ArrayList<>());
}
}
// 遍历map,生成菜单树
allMap.forEach((key, value) -> {
// 解决有些controller暂未使用,没有写接口方法的问题
if (!value.isEmpty()) {
// 如果是用户已有权限
if (map.containsKey(key)) {
// 遍历菜单树,把用户的权限的checked设置为true
for (PermissionTreeChildrenVO childrenVO : value) {
childrenVO.setChecked(map.get(key).contains(childrenVO));
}
} else {
// 遍历菜单树,把其他权限的checked设置为false
for (PermissionTreeChildrenVO childrenVO : value) {
childrenVO.setChecked(false);
}
}
}
Permission parent = permissionMapper.selectById(key);
PermissionTreeVO treeVO = new PermissionTreeVO();
treeVO.setId(parent.getId());
treeVO.setText(parent.getName());
treeVO.setState("open");
treeVO.setChildren(value);
tree.add(treeVO);
});
return tree;
}
@Override
public Page<RolePermission> selectByPage(RolePermissionPager pager) {
Page<RolePermission> page = new Page<>(pager.getPage(), pager.getRows());
QueryWrapper<RolePermission> wrapper = new QueryWrapper<>();
wrapper.eq(pager.getRoleId() != null, "role_id", pager.getRoleId());
wrapper.eq(StringUtils.isNotEmpty(pager.getPermissionId()), "permission_id", pager.getPermissionId());
return mapper.selectPage(page, wrapper);
}
@Override
public void init() {
mapper.deleteByRoleId(1);
// 查询全部一级权限
List<Permission> list = permissionMapper.selectByType(PermissionType.ZQX.getValue());
list.forEach(permission -> {
RolePermission rolePermission = new RolePermission();
rolePermission.setId(null);
rolePermission.setRoleId(1);
rolePermission.setPermissionId(permission.getId());
mapper.insert(rolePermission);
});
}
}
5、RoleServiceImpl
package cn.edu.sgu.www.mhxysy.service.system.impl;
import cn.edu.sgu.www.mhxysy.service.BaseService;
import cn.edu.sgu.www.mhxysy.dto.pager.RolePager;
import cn.edu.sgu.www.mhxysy.entity.system.Role;
import cn.edu.sgu.www.mhxysy.mapper.system.RoleMapper;
import cn.edu.sgu.www.mhxysy.service.system.RoleService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
@Service
public class RoleServiceImpl extends BaseService<Role> implements RoleService {
private final RoleMapper mapper;
@Autowired
public RoleServiceImpl(RoleMapper mapper) {
super(mapper);
this.mapper = mapper;
}
@Override
public boolean insert(Role role) {
super.insert(role);
return true;
}
@Override
public boolean deleteById(Integer id) {
super.deleteById(id);
return true;
}
@Override
public boolean updateById(Role role) {
super.updateById(role);
return true;
}
@Override
public List<Role> selectAll() {
return super.selectAll();
}
@Override
public Role selectById(String id) {
return mapper.selectById(id);
}
@Override
public Set<String> selectRolesByUsername(String username) {
return mapper.selectRolesByUsername(username);
}
@Override
public Page<Role> selectByPage(RolePager pager) {
Page<Role> page = new Page<>(pager.getPage(), pager.getRows());
QueryWrapper<Role> wrapper = new QueryWrapper<>();
return mapper.selectPage(page, wrapper);
}
}
主要的流程为:登录系统时,会查询登录用户的权限并保存到redis中,当新增或者删除了接口时,只需要点击前端权限列表页面的【初始化】按钮就可以重新加载系统的权限信息,再点击角色权限列表的对应按钮就可以重新为超管分配所有权限。只需要重新登录系统就可以重新获取用户的权限。页面效果如下图所示:
除此之外,还可以通过过滤器处理鉴权(推荐方式),可以参考以下文章。
springboot整合shiro实现认证和授权(非常详细)https://blog.csdn.net/heyl163_/article/details/131504939