package com.fdkankan.sale.config; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotRoleException; import cn.dev33.satoken.filter.SaServletFilter; import cn.dev33.satoken.jwt.StpLogicJwtForMixin; import cn.dev33.satoken.jwt.StpLogicJwtForStateless; import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.SaLoginConfig; import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.nacos.common.utils.HttpMethod; import com.fdkankan.redis.constant.RedisKey; import com.fdkankan.redis.util.RedisUtil; import com.fdkankan.sale.common.RedisKeyUtil; import com.fdkankan.sale.common.ResultCode; import com.fdkankan.sale.common.ResultData; import com.fdkankan.sale.entity.SysMenu; import com.fdkankan.sale.entity.User; import com.fdkankan.sale.exception.BusinessException; import com.fdkankan.sale.service.ISysMenuService; import com.fdkankan.sale.service.ISysUserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.List; @Configuration @Slf4j public class SaTokenConfigure { @Autowired RedisUtil redisUtil; @Autowired ISysMenuService sysMenuService; @Autowired ISysUserService sysUserService; // 注册Sa-Token的拦截器 @Bean public SaServletFilter getSaServletFilter() { return new SaServletFilter() // 指定 拦截路由 与 放行路由 .addInclude("/**").addExclude( "/**/sale/customer/**", "/**/sale/upload/**", "/**/test/**", "/**/sale/order/**", "/**/sale/repairInfo/**", "/**/recording/**") // 认证函数: 每次请求执行 .setAuth(obj -> { // 登录认证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 SaRouter.match("/**", "/service/manage/login", r ->checkLogin() ); List list = sysMenuService.list(); for (SysMenu o : list) { String url = o.getUrl(); String perm = o.getPerms(); if(StringUtils.isEmpty(url) || StringUtils.isEmpty(perm)){ continue; } if(StpUtil.hasRole("super-admin")){ continue; } SaRouter.match(url, r -> StpUtil.checkPermission(perm)); } }) // 异常处理函数:每次认证函数发生异常时执行此函数 .setError(e -> { SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); ResultData aj ; if (e instanceof NotLoginException) { // 如果是未登录异常 NotLoginException ee = (NotLoginException) e; aj = ResultData.error(ResultCode.USER_NOT_LOGIN.code(),ResultCode.USER_NOT_LOGIN.message()); } else if(e instanceof NotRoleException) { // 如果是角色异常 NotRoleException ee = (NotRoleException) e; aj = ResultData.error(ResultCode.NOT_ROLE.code(),ResultCode.NOT_ROLE.message()); } else if(e instanceof NotPermissionException) { // 如果是权限异常 NotPermissionException ee = (NotPermissionException) e; aj = ResultData.error(ResultCode.NOT_PERMISSION.code(),ResultCode.NOT_PERMISSION.message()); } else if(e instanceof BusinessException) { // 如果是权限异常 BusinessException ee = (BusinessException) e; aj = ResultData.error(ee.getCode(),ee.getMessage()); } else { // 普通异常, 输出:500 + 异常信息 aj = ResultData.error(ResultCode.SYSTEM_ERROR.code(),ResultCode.SYSTEM_ERROR.message()); } return JSONObject.toJSONString(aj); }) // 前置函数:在每次认证函数之前执行 .setBeforeAuth(r -> { // ---------- 设置一些安全响应头 ---------- SaHolder.getResponse() .setHeader("Access-Control-Allow-Origin", "*") .setHeader("Access-Control-Allow-Methods", "*") .setHeader("Access-Control-Max-Age", "3600") .setHeader("Access-Control-Allow-Headers", "*") .setServer("4dkk"); // 跳过对 OPTIONS 请求的检查,否则这里会鉴权失败,导致 springboot 配置的 addCorsMappings 跨域不执行 if (SaHolder.getRequest().getMethod().equals(HttpMethod.OPTIONS.toString())) { SaRouter.back(); } }); } private void checkLogin(){ String redisKey = String.format(RedisKeyUtil.loginToken,StpUtil.getTokenValue()); if(!redisUtil.hasKey(redisKey)){ throw new BusinessException(ResultCode.USER_NOT_LOGIN); } //sysUserService.saveByRedisKey(redisKey); redisUtil.expire(String.format(RedisKeyUtil.loginToken,StpUtil.getTokenValue()),21600); StpUtil.checkLogin(); } //Sa-Token 整合 jwt //Stateless 无状态模式 纯jwt //Mixin 混入模式 jwt 与 Redis 逻辑混合 //Simple 简单模式 Token风格替换 @Bean public StpLogic getStpLogicJwt() { return new StpLogicJwtForMixin(); } }