package com.fdkankan.fusion.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.router.SaRouter; import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.aliyun.oss.HttpMethod; import com.fdkankan.fusion.common.ResultCode; import com.fdkankan.fusion.common.ResultData; import com.fdkankan.fusion.common.util.RedisKeyUtil; import com.fdkankan.fusion.controller.LoginController; import com.fdkankan.fusion.exception.BusinessException; import com.fdkankan.fusion.httpClient.FdService; import com.fdkankan.fusion.httpClient.response.FdkkLoginVo; import com.fdkankan.fusion.service.ITmPermissionService; import com.fdkankan.fusion.service.ITmUserService; import com.fdkankan.redis.constant.RedisKey; import com.fdkankan.redis.util.RedisUtil; 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; @Configuration @Slf4j public class SaTokenConfigure { @Autowired RedisUtil redisUtil; @Autowired FdService fdService; // 注册Sa-Token的拦截器 @Bean public SaServletFilter getSaServletFilter() { return new SaServletFilter() // 指定 拦截路由 与 放行路由 .addInclude("/**").addExclude("/**/test/**","/**/inner/**","/**/notAuth/**") // 认证函数: 每次请求执行 .setAuth(obj -> { String share = SaHolder.getRequest().getHeader("share"); if(StringUtils.isNotBlank(share) && "1".equals(share)){ //分享请求头 return; } SaRouter.match("/sceneDownLog/list", r -> StpUtil.checkRole("admin-super")); // 登录认证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 SaRouter.match("/**", "/fdLogin", r ->checkLogin() ); String menu = redisUtil.get(RedisKey.MANAGE_MENU); if(StringUtils.isBlank(menu)){ SaRouter.match("/**", r -> StpUtil.checkRole("admin-super")); return; } JSONArray menuArray = JSONObject.parseArray(menu); for (Object o : menuArray) { JSONObject jsonObject = (JSONObject) o; String url = jsonObject.getString("url"); String perm = jsonObject.getString("perms"); if(StringUtils.isEmpty(url) || StringUtils.isEmpty(perm)){ continue; } if(StpUtil.hasRole("admin-super")){ 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); } else if(e instanceof NotRoleException) { // 如果是角色异常 NotRoleException ee = (NotRoleException) e; aj = ResultData.error(ResultCode.NOT_PERMISSION); } else if(e instanceof NotPermissionException) { // 如果是权限异常 NotPermissionException ee = (NotPermissionException) e; aj = ResultData.error(ResultCode.NOT_PERMISSION); } else if(e instanceof BusinessException) { // 如果是权限异常 BusinessException ee = (BusinessException) e; aj = ResultData.error(ee.getCode(),ee.getMessage()); } else { // 普通异常, 输出:500 + 异常信息 aj = ResultData.error(ResultCode.SYSTEM_ERROR); } 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(){ if(!redisUtil.hasKey(String.format(RedisKeyUtil.fusionLoginToken,StpUtil.getTokenValue()))){ throw new BusinessException(ResultCode.USER_NOT_LOGIN); } redisUtil.expire(String.format(RedisKeyUtil.fusionLoginToken,StpUtil.getTokenValue()),21600); if(!redisUtil.hasKey(String.format(RedisKeyUtil.fusionLoginUser,StpUtil.getLoginId()))){ throw new BusinessException(ResultCode.USER_NOT_LOGIN); } String value = redisUtil.get(String.format(RedisKeyUtil.fusionLoginUser,StpUtil.getLoginId())); FdkkLoginVo fdkkLoginVo = JSONObject.parseObject(value, FdkkLoginVo.class); if(fdkkLoginVo.getTmUser().getStatus() == 0){ throw new BusinessException(ResultCode.USER_NOT_LOGIN_PERM); } redisUtil.expire(String.format(RedisKeyUtil.fusionLoginUser,StpUtil.getLoginId()),21600); //校验fdtoken if(!redisUtil.hasKey(String.format(RedisKeyUtil.fdToken,fdService.getFdToken()))){ throw new BusinessException(ResultCode.USER_NOT_LOGIN); } redisUtil.expire(String.format(RedisKeyUtil.fdToken,fdService.getFdToken()),21600); StpUtil.checkLogin(); } //Sa-Token 整合 jwt //Stateless 无状态模式 纯jwt //Mixin 混入模式 jwt 与 Redis 逻辑混合 //Simple 简单模式 Token风格替换 @Bean public StpLogic getStpLogicJwt() { return new StpLogicJwtForMixin(); } }