feat(新增): 判断用户请求路径是否匹配

This commit is contained in:
bunny 2024-09-27 16:49:01 +08:00
parent 2ca73b5aa4
commit 388579409e
27 changed files with 559 additions and 154 deletions

View File

@ -10,16 +10,17 @@ import java.util.Collections;
public class NewCodeGet {
// 数据连接
public static final String sqlHost = "jdbc:mysql://106.15.251.123:3305/bunny_docs?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true";
public static final String sqlHost = "jdbc:mysql://192.168.3.98:3304/auth_admin?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true";
// 作者名称
public static final String author = "Bunny";
// 公共路径
public static final String outputDir = "F:\\web项目\\PC\\BunnyNote\\BunnyBBS-server\\service\\service-web";
public static final String outputDir = "D:\\MyFolder\\auth-admin\\auth-server-java\\service";
// public static final String outputDir = "D:\\Project\\web\\PC\\auth\\auth-server\\services";
// 实体类名称
public static final String entity = "Bunny";
public static void main(String[] args) {
Generation("article");
Generation("sys_router");
}
/**
@ -38,27 +39,25 @@ public class NewCodeGet {
// 指定输出目录
.outputDir(outputDir + "/src/main/java");
})
.packageConfig(builder -> {
builder.entity(entity)// 实体类包名
// TODO 父包名如果为空将下面子包名必须写全部 否则就只需写子包名
.parent("cn.bunny.service.web")
.controller("controller")// 控制层包名
.mapper("mapper")// mapper层包名
.service("service")// service层包名
.serviceImpl("service.impl")// service实现类包名
// 自定义mapper.xml文件输出目录
.pathInfo(Collections.singletonMap(OutputFile.xml, outputDir + "/src/main/resources/mapper"));
})
.packageConfig(builder -> builder.entity(entity)// 实体类包名
// 父包名如果为空将下面子包名必须写全部 否则就只需写子包名
.parent("cn.bunny.services")
.controller("controller")// 控制层包名
.mapper("mapper")// mapper层包名
.service("service")// service层包名
.serviceImpl("service.impl")// service实现类包名
// 自定义mapper.xml文件输出目录
.pathInfo(Collections.singletonMap(OutputFile.xml, outputDir + "/src/main/resources/mapper")))
.strategyConfig(builder -> {
// 设置要生成的表名
builder.addInclude(tableName)
//.addTablePrefix("sys_")// TODO 设置表前缀过滤
.addTablePrefix("sys_")
.entityBuilder()
.enableLombok()
.enableChainModel()
.naming(NamingStrategy.underline_to_camel)// 数据表映射实体命名策略默认下划线转驼峰underline_to_camel
.columnNaming(NamingStrategy.underline_to_camel)// 表字段映射实体属性命名规则默认null不指定按照naming执行
.idType(IdType.AUTO)// TODO 添加全局主键类型
.idType(IdType.ASSIGN_ID)// 添加全局主键类型
.formatFileName("%s")// 格式化实体名称%s取消首字母I,
.mapperBuilder()
.mapperAnnotation(Mapper.class)// 开启mapper注解
@ -72,12 +71,6 @@ public class NewCodeGet {
.controllerBuilder()
.enableRestStyle();
})
// .injectionConfig(consumer -> {
// Map<String, String> customFile = new HashMap<>();
// // 配置DTO需要的话但是需要有能配置Dto的模板引擎比如freemarker但是这里我们用的VelocityEngine因此不多作介绍
// customFile.put(outputDir, "/src/main/resources/templates/entityDTO.java.ftl");
// consumer.customFile(customFile);
// })
.execute();
}
}

View File

@ -1,57 +0,0 @@
package cn.bunny.common.generator;
public class OldCodeGet {
public static void main(String[] args) {
// // 1创建代码生成器
// AutoGenerator mpg = new AutoGenerator();
//
// // 2全局配置
// // 全局配置
// GlobalConfig gc = new GlobalConfig();
// // TODO 需要修改路径名称
// gc.setOutputDir("F:\\web项目\\Bunny-Cli\\Java\\java-template\\service" + "/src/main/java");
// gc.setServiceName("%sService"); // 去掉Service接口的首字母I
// gc.setAuthor("bunny");
// gc.setOpen(false);
// mpg.setGlobalConfig(gc);
//
// // 3数据源配置
// DataSourceConfig dsc = new DataSourceConfig();
// // TODO 需要修改数据库
// dsc.setUrl("jdbc:mysql://106.15.251.123:3305/guigu-oa?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true");
// dsc.setDriverName("com.mysql.cj.jdbc.Driver");
// dsc.setUsername("root");
// dsc.setPassword("02120212");
// dsc.setDbType(DbType.MYSQL);
// mpg.setDataSource(dsc);
//
// // 4包配置
// PackageConfig pc = new PackageConfig();
// pc.setParent("cn.bunny");
// // TODO 需要修改模块名
// pc.setModuleName("service");
// pc.setController("controller");
// pc.setService("service");
// pc.setMapper("mapper");
// mpg.setPackageInfo(pc);
//
// // 5策略配置
// StrategyConfig strategy = getStrategyConfig();
// mpg.setStrategy(strategy);
//
// // 6执行
// mpg.execute();
// }
//
// private static StrategyConfig getStrategyConfig() {
// StrategyConfig strategy = new StrategyConfig();
// // TODO 要生成的表
// strategy.setInclude("sys_menu", "sys_role_menu");
// strategy.setNaming(NamingStrategy.underline_to_camel);// 数据库表映射到实体的命名策略
// strategy.setColumnNaming(NamingStrategy.underline_to_camel);// 数据库表字段映射到实体的命名策略
// strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
// strategy.setRestControllerStyle(true); // restful api风格控制器
// strategy.setControllerMappingHyphenStyle(true); // url中驼峰转连字符
// return strategy;
}
}

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.bunny</groupId>
<artifactId>bunny-template</artifactId>
<artifactId>auth-server-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.bunny</groupId>
<artifactId>bunny-template</artifactId>
<artifactId>auth-server-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

View File

@ -1,26 +1,42 @@
package cn.bunny.dao.entity.system;
import cn.bunny.dao.entity.BaseEntity;
import com.alibaba.fastjson2.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author Bunny
* @since 2024-09-27
*/
@Getter
@Setter
@Accessors(chain = true)
@TableName("sys_router")
@ApiModel(value = "Router对象", description = "系统菜单表")
@ApiModel(value = "Router对象", description = "系统路由")
public class Router extends BaseEntity {
@ApiModelProperty("在项目中路径")
private String routerPath;
private String path;
@ApiModelProperty("路由名称")
private String routeName;
@ApiModelProperty("父级id")
@JsonProperty("parentId")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long parentId;
@ApiModelProperty("路由title")
@ -34,4 +50,5 @@ public class Router extends BaseEntity {
@ApiModelProperty("是否显示")
private Boolean visible;
}
}

View File

@ -0,0 +1,38 @@
package cn.bunny.dao.vo.common;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "KvData对象", description = "基础键值对数据")
public class KvData {
@ApiModelProperty(value = "id", name = "主键")
@JsonProperty("id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long id;
@ApiModelProperty("值内容")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Object value;
@ApiModelProperty("值内容")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Object key;
@ApiModelProperty("显示内容")
private String label;
}

View File

@ -0,0 +1,25 @@
package cn.bunny.dao.vo.common;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "TreeSelectList对象", description = "树形结构数据")
public class TreeSelectVo extends KvData {
@ApiModelProperty("父级id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long parentId;
@ApiModelProperty("子级内容")
private List<TreeSelectVo> children;
}

View File

@ -0,0 +1,46 @@
package cn.bunny.dao.vo.router;
import cn.bunny.dao.vo.BaseVo;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "RouterControllerVo对象", description = "路由管理端返回对象")
public class RouterControllerVo extends BaseVo {
@ApiModelProperty("在项目中路径")
private String path;
@ApiModelProperty("路由名称")
private String routeName;
@ApiModelProperty("父级id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long parentId;
@ApiModelProperty("title名称")
private String title;
@ApiModelProperty("图标")
private String icon;
@ApiModelProperty("等级")
private Integer routerRank;
@ApiModelProperty("是否显示")
private Boolean visible;
@ApiModelProperty("子路由")
private List<RouterControllerVo> children;
}

View File

@ -0,0 +1,33 @@
package cn.bunny.dao.vo.router;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "UserRouterMetaVo对象", description = "系统属性内容")
public class RouterMeta {
@ApiModelProperty(value = "图标")
private String icon;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "排序权重")
private Integer rank;
@ApiModelProperty(value = "角色列表")
private List<String> roles;
@ApiModelProperty(value = "权限列表")
private List<String> auths;
}

View File

@ -0,0 +1,47 @@
package cn.bunny.dao.vo.router;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(value = "UserRouterVo对象", description = "系统菜单表")
public class UserRouterVo {
@Schema(name = "id", title = "主键")
@JsonProperty("id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long id;
@ApiModelProperty("父级id")
@JsonProperty("parentId")
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JSONField(serializeUsing = ToStringSerializer.class)
private Long parentId;
@ApiModelProperty("在项目中路径")
private String path;
@ApiModelProperty("路由名称")
@JsonProperty("name")
private String routeName;
@ApiModelProperty("meta内容")
private RouterMeta meta;
@ApiModelProperty("子路由")
private List<UserRouterVo> children;
}

View File

@ -4,6 +4,7 @@ import cn.bunny.dao.vo.BaseVo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
/**
@ -16,8 +17,8 @@ import java.util.List;
@Builder
@Schema(name = "LoginVo对象", title = "登录成功返回内容", description = "登录成功返回内容")
public class LoginVo extends BaseVo {
@Schema(name = "nickName", title = "昵称")
private String nickName;
@Schema(name = "nickname", title = "昵称")
private String nickname;
@Schema(name = "username", title = "用户名")
private String username;
@ -40,24 +41,12 @@ public class LoginVo extends BaseVo {
@Schema(name = "personDescription", title = "个人描述")
private String personDescription;
@Schema(name = "articleMode", title = "文章显示模式")
private String articleMode;
@Schema(name = "layout", title = "页面布局方式")
private String layout;
@Schema(name = "lastLoginIp", title = "最后登录IP")
private String lastLoginIp;
@Schema(name = "lastLoginIpAddress", title = "最后登录ip地址")
private String lastLoginIpAddress;
@Schema(name = "totalIntegral", title = "积分")
private Integer totalIntegral;
@Schema(name = "currentIntegral", title = "当前积分")
private Integer currentIntegral;
@Schema(name = "status", title = "1:禁用 0:正常")
private Byte status;
@ -71,8 +60,9 @@ public class LoginVo extends BaseVo {
private String expires;
@Schema(name = "roleList", title = "角色列表")
private List<String> roleList;
private List<String> roles = new ArrayList<>();
@Schema(name = "powerList", title = "权限列表")
private List<String> powerList;
private List<String> permissions = new ArrayList<>();
}

View File

@ -9,7 +9,7 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.bunny</groupId>
<artifactId>bunny-template</artifactId>
<artifactId>auth-server-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>bunny-template</name>

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.bunny</groupId>
<artifactId>bunny-template</artifactId>
<artifactId>auth-server-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

View File

@ -1,9 +1,17 @@
package cn.bunny.services.controller;
import cn.bunny.dao.pojo.result.Result;
import cn.bunny.dao.vo.router.UserRouterVo;
import cn.bunny.services.service.RouterService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 系统菜单表 前端控制器
@ -17,4 +25,13 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("admin/router")
public class RouterController {
@Autowired
private RouterService routerService;
@Operation(summary = "获取用户菜单", description = "获取用户菜单")
@GetMapping("getRouterAsync")
public Result<List<UserRouterVo>> getRouterAsync() {
List<UserRouterVo> voList = routerService.getRouterAsync();
return Result.success(voList);
}
}

View File

@ -18,8 +18,9 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
@Transactional
@ -51,15 +52,30 @@ public class UserFactory {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS_SLASH);
String expires = plusDay.format(dateTimeFormatter);
// 查找用户橘色
List<String> roles = roleMapper.selectListByUserId(userId).stream().map(Role::getRoleCode).toList();
List<String> permissions = new ArrayList<>();
boolean isAdmin = roles.stream().anyMatch(role -> role.equals("admin"));
if (isAdmin) {
permissions.add("*");
permissions.add("*::*");
permissions.add("*::*::*");
} else {
permissions = powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).toList();
}
// 构建返回对象
LoginVo loginVo = new LoginVo();
BeanUtils.copyProperties(user, loginVo);
loginVo.setNickname(user.getNickName());
loginVo.setToken(token);
loginVo.setRefreshToken(token);
loginVo.setLastLoginIp(IpUtil.getCurrentUserIpAddress().getRemoteAddr());
loginVo.setLastLoginIpAddress(IpUtil.getCurrentUserIpAddress().getIpRegion());
loginVo.setRoleList(roleMapper.selectListByUserId(userId).stream().map(Role::getRoleCode).collect(Collectors.toList()));
loginVo.setPowerList(powerMapper.selectListByUserId(userId).stream().map(Power::getPowerCode).collect(Collectors.toList()));
loginVo.setRoles(roles);
loginVo.setPermissions(permissions);
loginVo.setUpdateUser(userId);
loginVo.setExpires(expires);

View File

@ -24,4 +24,12 @@ public interface PowerMapper extends BaseMapper<Power> {
*/
@NotNull
List<Power> selectListByUserId(long userId);
/**
* * 根据权限码查询可以访问URL
*
* @param powerCodes 权限码
* @return 权限列表
*/
List<Power> selectListByPowerCodes(List<String> powerCodes);
}

View File

@ -4,15 +4,32 @@ import cn.bunny.dao.entity.system.Router;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* <p>
* 系统菜单表 Mapper 接口
* Mapper 接口
* </p>
*
* @author Bunny
* @since 2024-09-26
* @since 2024-09-27
*/
@Mapper
public interface RouterMapper extends BaseMapper<Router> {
/**
* * 根据用户id查找路由内容
*
* @param userId 用户id
* @return 路由列表
*/
List<Long> selectListByUserId(Long userId);
/**
* * 递归查询所有父级Id直到查询到父级Id为0
*
* @param ids id列表
* @return 路由列表
*/
List<Router> selectParentListByRouterId(List<Long> ids);
}

View File

@ -76,7 +76,7 @@ public class WebSecurityConfig {
.addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, redisTemplate, customUserDetailsService), UsernamePasswordAuthenticationFilter.class)
// 其它权限鉴权过滤器
.addFilterAt(new NoTokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class)
.addFilterAt(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class)
.addFilterAt(new TokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
// 自定义密码加密器和用户登录
.passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService);

View File

@ -8,10 +8,12 @@ import cn.bunny.dao.pojo.constant.RedisUserConstant;
import cn.bunny.dao.pojo.result.Result;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import cn.bunny.dao.vo.user.LoginVo;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.OncePerRequestFilter;
@ -25,7 +27,7 @@ public class NoTokenAuthenticationFilter extends OncePerRequestFilter {
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
// 判断是否有 token
String token = request.getHeader("token");
if (token == null) {
@ -40,10 +42,11 @@ public class NoTokenAuthenticationFilter extends OncePerRequestFilter {
return;
}
// token 存在查找 Redis
// 查找 Redis
String username = JwtHelper.getUsername(token);
Long userId = JwtHelper.getUserId(token);
LoginVo loginVo = (LoginVo) redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username));
Object loginVoObject = redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username));
LoginVo loginVo = JSON.parseObject(JSON.toJSONString(loginVoObject), LoginVo.class);
// 判断用户是否禁用
if (loginVo != null && loginVo.getStatus() == 1) {

View File

@ -1,10 +1,12 @@
package cn.bunny.services.security.filter;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.dao.vo.user.LoginVo;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
@ -17,16 +19,10 @@ import java.util.List;
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final RedisTemplate<String, Object> redisTemplate;
public TokenAuthenticationFilter(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// 自定义实现内容
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
UsernamePasswordAuthenticationToken authentication = getAuthentication();
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
@ -35,21 +31,20 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
/**
* 用户请求判断
*
* @param request 请求
* @return 验证码方法
*/
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
private UsernamePasswordAuthenticationToken getAuthentication() {
// 请求头是否有token
String token = request.getHeader("token");
String username = "admin";
List<SimpleGrantedAuthority> authList = new ArrayList<>();
LoginVo LoginVo = BaseContext.getLoginVo();
// 设置角色内容
if (token != null) {
List<String> roleList = new ArrayList<>();
return new UsernamePasswordAuthenticationToken(username, null, authList);
} else {
return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
}
// 通过username从redis获取权限数据
String username = LoginVo.getUsername();
List<String> roleList = LoginVo.getRoles();
// 角色列表
List<SimpleGrantedAuthority> authList = roleList.stream().map(SimpleGrantedAuthority::new).toList();
if (authList.isEmpty()) return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
return new UsernamePasswordAuthenticationToken(username, null, authList);
}
}

View File

@ -21,6 +21,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import java.io.IOException;
@ -76,10 +77,15 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
}
}
/**
* * 验证成功
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) {
// 获取登录返回信息
LoginVo loginVo = customUserDetailsService.login(loginDto);
// 判断用户是否禁用
if (loginVo.getStatus() == 1) {
out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED));
return;
@ -88,15 +94,17 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
out(response, Result.success(loginVo));
}
/**
* 验证失败
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
String password = loginDto.getPassword();
String username = loginDto.getUsername();
if (password == null || username == null || password.isBlank() || username.isBlank()) {
if (!StringUtils.hasText(password) || !StringUtils.hasText(username))
out(response, Result.error(ResultCodeEnum.USERNAME_OR_PASSWORD_NOT_EMPTY));
} else {
else
out(response, Result.error(null, ResultCodeEnum.LOGIN_ERROR));
}
}
}

View File

@ -1,6 +1,9 @@
package cn.bunny.services.security.service.iml;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.utils.JwtHelper;
import cn.bunny.dao.entity.system.Power;
import cn.bunny.services.mapper.PowerMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authorization.AuthorizationDecision;
@ -21,27 +24,50 @@ import java.util.function.Supplier;
@Component
@Slf4j
public class CustomAuthorizationManagerServiceImpl implements AuthorizationManager<RequestAuthorizationContext> {
private final PowerMapper powerMapper;
public CustomAuthorizationManagerServiceImpl(PowerMapper powerMapper) {this.powerMapper = powerMapper;}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
// 用户的token和用户id请求Url
HttpServletRequest request = context.getRequest();
String token = request.getHeader("token");
Long userId = JwtHelper.getUserId(token);// 用户id
String requestURI = request.getRequestURI();// 请求地址
String method = request.getMethod();// 请求方式
List<String> roleCodeList = authentication.get().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();// 角色代码列表
return new AuthorizationDecision(hasRoleList(requestURI, method, userId));
// 用户id
Long userId = JwtHelper.getUserId(token);
// 请求地址
String requestURI = request.getRequestURI();
// 请求方式
String method = request.getMethod();
// 角色代码列表
List<String> roleCodeList = authentication.get().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
// 校验权限
return new AuthorizationDecision(hasAuth(requestURI, roleCodeList));
}
/**
* 查询用户所属的角色信息
*
* @param requestURI 请求url地址
* @param method 请求方式
* @param userId 用户id
* @param requestURI 请求url地址
* @param roleCodeList 角色列表
*/
private Boolean hasRoleList(String requestURI, String method, Long userId) {
return true;
private Boolean hasAuth(String requestURI, List<String> roleCodeList) {
// 判断是否是 admin
boolean isAdmin = roleCodeList.stream().anyMatch(role -> role.equals("admin"));
if (isAdmin) return true;
// 不是 admin查询角色权限关系表
List<String> powerCodes = BaseContext.getLoginVo().getPermissions();
// 根据权限码查询可以访问URL
List<Power> powerList = powerMapper.selectListByPowerCodes(powerCodes);
// 判断是否与请求路径匹配
return powerList.stream().anyMatch(power -> power.getRequestUrl().equals(requestURI));
}
}

View File

@ -1,16 +1,24 @@
package cn.bunny.services.service;
import cn.bunny.dao.entity.system.Router;
import cn.bunny.dao.vo.router.UserRouterVo;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 系统菜单表 服务类
* 服务类
* </p>
*
* @author Bunny
* @since 2024-09-26
* @since 2024-09-27
*/
public interface RouterService extends IService<Router> {
/**
* * 获取路由内容
*
* @return 路遇列表
*/
List<UserRouterVo> getRouterAsync();
}

View File

@ -1,20 +1,103 @@
package cn.bunny.services.service.impl;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.exception.BunnyException;
import cn.bunny.dao.entity.system.Router;
import cn.bunny.dao.pojo.constant.RedisUserConstant;
import cn.bunny.dao.pojo.result.ResultCodeEnum;
import cn.bunny.dao.vo.router.RouterMeta;
import cn.bunny.dao.vo.router.UserRouterVo;
import cn.bunny.dao.vo.user.LoginVo;
import cn.bunny.services.mapper.RouterMapper;
import cn.bunny.services.service.RouterService;
import cn.bunny.services.service.process.RouterServiceProcess;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 系统菜单表 服务实现类
* 服务实现类
* </p>
*
* @author Bunny
* @since 2024-09-26
* @since 2024-09-27
*/
@Service
@Transactional
public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> implements RouterService {
@Autowired
private RouterServiceProcess routerServiceProcess;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* * 获取路由内容
*
* @return 路遇列表
*/
@Override
public List<UserRouterVo> getRouterAsync() {
// 当前用户id
String username = BaseContext.getUsername();
LoginVo loginVo = (LoginVo) redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username));
if (loginVo == null) throw new BunnyException(ResultCodeEnum.FAIL);
// 角色列表
List<String> roleList = loginVo.getRoles();
// 权限列表
List<String> powerCodeList = loginVo.getPermissions();
// 路由列表根据用户角色判断
List<Router> routerList;
// 返回路由列表
List<UserRouterVo> list = new ArrayList<>();
// 查询用户角色判断是否是管理员角色
boolean isAdmin = roleList.stream().anyMatch(authUserRole -> authUserRole.equals("admin"));
if (isAdmin) {
routerList = list();
} else {
List<Long> routerIds = baseMapper.selectListByUserId(loginVo.getId());
routerList = baseMapper.selectParentListByRouterId(routerIds);
}
// 构建返回路由列表
List<UserRouterVo> routerVoList = routerList.stream()
.filter(Router::getVisible)
.map(router -> {
// 复制对象
UserRouterVo routerVo = new UserRouterVo();
BeanUtils.copyProperties(router, routerVo);
routerVo.setPath(router.getPath().trim());
// 设置
RouterMeta meta = RouterMeta.builder()
.rank(router.getRouterRank())
.icon(router.getIcon())
.title(router.getTitle())
.roles(roleList)
.auths(powerCodeList).build();
routerVo.setMeta(meta);
return routerVo;
}).distinct().toList();
// 构建树形结构
routerVoList.forEach(routerVo -> {
if (routerVo.getParentId() == 0) {
routerVo.setChildren(routerServiceProcess.handleGetChildrenWIthRouter(routerVo.getId(), routerVoList));
list.add(routerVo);
}
});
return list;
}
}

View File

@ -0,0 +1,62 @@
package cn.bunny.services.service.process;
import cn.bunny.dao.vo.common.TreeSelectVo;
import cn.bunny.dao.vo.router.RouterControllerVo;
import cn.bunny.dao.vo.router.UserRouterVo;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class RouterServiceProcess {
/**
* * 递归调用设置子路由
*
* @param id 主键
* @param routerVoList 返回VO列表
* @return 返回路由列表
*/
public List<UserRouterVo> handleGetChildrenWIthRouter(Long id, @NotNull List<UserRouterVo> routerVoList) {
List<UserRouterVo> list = new ArrayList<>();
for (UserRouterVo routerVo : routerVoList) {
if (routerVo.getParentId().equals(id)) {
routerVo.setChildren(handleGetChildrenWIthRouter(routerVo.getId(), routerVoList));
list.add(routerVo);
}
}
return list;
}
/**
* * 递归调用设置管理子路由
*
* @param nodeId 主键
* @param nodeList 返回VO列表
* @return 返回路由列表
*/
public List<RouterControllerVo> handleGetChildrenWithRouterControllerVo(Long nodeId, List<RouterControllerVo> nodeList) {
return nodeList.stream()
.filter(node -> node.getParentId().equals(nodeId))
.peek(node -> node.setChildren(handleGetChildrenWithRouterControllerVo(node.getId(), nodeList)))
.toList();
}
/**
* * 整理树形结构
*
* @param nodeId 节点ID
* @param nodeList 节点列表
* @return 树形列表
*/
public List<TreeSelectVo> handleGetTreeSelectList(Object nodeId, @NotNull List<TreeSelectVo> nodeList) {
// 使用 Stream API 找到所有子节点
return nodeList.stream()
.filter(node -> node.getParentId().equals(nodeId))
.peek(node -> node.setChildren(handleGetTreeSelectList(node.getValue(), nodeList)))
.toList();
}
}

View File

@ -33,5 +33,10 @@
AND rp.power_id = p.id
AND u.id = #{userId}
</select>
<select id="selectListByPowerCodes" resultType="cn.bunny.dao.entity.system.Power">
SELECT * FROM sys_power WHERE power_code IN
<foreach collection="powerCodes" separator="," item="code" open="(" close=")">
#{code}
</foreach>
</select>
</mapper>

View File

@ -5,7 +5,7 @@
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="cn.bunny.dao.entity.system.Router">
<id column="id" property="id"/>
<result column="router_path" property="routerPath"/>
<result column="path" property="path"/>
<result column="route_name" property="routeName"/>
<result column="parent_id" property="parentId"/>
<result column="title" property="title"/>
@ -14,14 +14,39 @@
<result column="visible" property="visible"/>
<result column="create_user" property="createUser"/>
<result column="update_user" property="updateUser"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
<result column="create_time" property="createTime"/>
<result column="is_deleted" property="isDeleted"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, router_path, route_name, parent_id, title, icon, router_rank, visible, create_user, update_user, create_time, update_time, is_deleted
id, path, route_name, parent_id, title, icon, router_rank, visible, create_user, update_user, update_time, create_time, is_deleted
</sql>
<select id="selectListByUserId" resultType="java.lang.Long">
SELECT router.id
FROM sys_user_role user_role
LEFT JOIN sys_user user ON user_role.user_id = user.id
LEFT JOIN sys_router_role router_role ON user_role.role_id = router_role.role_id
LEFT JOIN sys_router router ON router_role.router_id = router.id
WHERE user.id = #{userId}
</select>
<!-- 递归查询所有父级Id直到查询到父级Id为0 -->
<select id="selectParentListByRouterId" resultType="cn.bunny.dao.entity.system.Router">
WITH RECURSIVE ParentChain AS (
SELECT * FROM sys_router
WHERE id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
UNION ALL
SELECT r.* FROM sys_router r
INNER JOIN ParentChain pc ON r.id = pc.parent_id
)
SELECT
<include refid="Base_Column_List"/>
FROM ParentChain;
</select>
</mapper>