• 登录以及权限系统,用户认证信息存储设计思维


    一,登录

    1. 首先在system模块提供一个统一的接口,入参为一个用户名,出参是该用户的所有信息,包括用户名,密码,以及该用户在每个公司所有的角色,以及权限信息,可将反参缓存入NOSQL数据库中
    2. 在登录模块中先去缓存中获取看该用户的用户信息是否有缓存,有的话,直接返回,没有的话远程调用上述1的接口获取吗,然后通过返回的信息和登录接口的信息进行对比校验,如果都正确,通过jwt工具返回token

    二,用户权限校验,用户认证信息存储

    (1)通过全局拦截器,及网关设计

    1. 首先网关会拦截所有的请求,然后我们可以在网关模块定义一个放行白名单,比如一些登录接口,引用swagger页面的接口进行放行
    2. 拦截下来的接口,我们去获取它的请求头看是否有token,没有的话,直接返回未授权
    3. 有的话继续下边的操作,解析token,通过解析出来的用户信息,通过用户名从system获取该用户的所有信息
    4. 然后先校验token中解析出来的密码与system查询出来的密码是否一致
    5. 如果一致,再进行下一步鉴权,通过system查询出来的用户信息,查询该用户在所有公司所有的权限信息,然后和当前请求的权限信息进行对比,如果发现当前请求url与查询出来的所有的url都不匹配,则返回未授权,若匹配,则将登录认证信息,比如用户名,密码,公司信息,若有多语种,还可添加语言等加入请求头中

    代码如下:包括如何定义过滤器,以及上述操作

    定义过滤器:

    1. package sca.pro.gateway.common.auth;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.cloud.gateway.filter.GatewayFilter;
    4. import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    5. import org.springframework.stereotype.Component;
    6. /**
    7. * @author shq
    8. * @description 自定义token认证过滤器工厂
    9. * @createDate 2022-5-25
    10. * @updateUser
    11. * @updateDate
    12. * @updateRemark
    13. */
    14. @Component
    15. public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory {
    16. @Autowired
    17. private AuthGatewayFilter authGatewayFilter;
    18. @Override
    19. public GatewayFilter apply(Object config) {
    20. return authGatewayFilter;
    21. }
    22. }
      1. package sca.pro.gateway.common.auth;
      2. import cn.hutool.json.JSONUtil;
      3. import com.fasterxml.jackson.databind.ObjectMapper;
      4. import org.springframework.beans.factory.annotation.Autowired;
      5. import org.springframework.boot.context.properties.EnableConfigurationProperties;
      6. import org.springframework.cloud.gateway.filter.GatewayFilter;
      7. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
      8. import org.springframework.core.Ordered;
      9. import org.springframework.core.io.buffer.DataBuffer;
      10. import org.springframework.data.redis.core.StringRedisTemplate;
      11. import org.springframework.data.redis.core.ValueOperations;
      12. import org.springframework.http.HttpStatus;
      13. import org.springframework.http.server.reactive.ServerHttpRequest;
      14. import org.springframework.http.server.reactive.ServerHttpResponse;
      15. import org.springframework.stereotype.Component;
      16. import org.springframework.util.AntPathMatcher;
      17. import org.springframework.util.CollectionUtils;
      18. import org.springframework.util.MultiValueMap;
      19. import org.springframework.web.server.ServerWebExchange;
      20. import reactor.core.publisher.Flux;
      21. import reactor.core.publisher.Mono;
      22. import sca.pro.common.contants.Contants;
      23. import sca.pro.common.redis.authInfo.AuthInfo;
      24. import sca.pro.common.redis.authInfo.CompanyInfo;
      25. import sca.pro.common.response.HttpCode;
      26. import sca.pro.common.response.HttpResult;
      27. import sca.pro.common.utils.PasswordUtils;
      28. import sca.pro.common.utils.SystemUtils;
      29. import sca.pro.gateway.feign.SystemFeignService;
      30. import javax.annotation.PostConstruct;
      31. import javax.annotation.Resource;
      32. import java.io.File;
      33. import java.security.PrivateKey;
      34. import java.security.PublicKey;
      35. import java.util.*;
      36. import java.util.stream.Collectors;
      37. /**
      38. * @author shq
      39. * @description 身份认证过滤器
      40. * @createDate 2022-5-25
      41. * @updateUser
      42. * @updateDate
      43. * @updateRemark
      44. */
      45. @Component
      46. @EnableConfigurationProperties(JwtProperties.class)
      47. public class AuthGatewayFilter implements GatewayFilter, Ordered {
      48. @Resource
      49. private JwtProperties jwtProperties;
      50. @Autowired
      51. private StringRedisTemplate stringRedisTemplate;
      52. @Autowired
      53. private SystemFeignService systemFeignService;
      54. // spring的路径匹配器
      55. private final static AntPathMatcher antPathMatcher = new AntPathMatcher();
      56. private static PublicKey publicKey;
      57. private static PrivateKey privateKey;
      58. /**
      59. * 初始化公私钥
      60. *
      61. * @throws Exception
      62. */
      63. @PostConstruct
      64. public void init() throws Exception {
      65. boolean flag = false;
      66. String path = SystemUtils.getApplicationPath() + Contants.JWT_RSAKEY_PATH;
      67. String pubPath = path + "/rsa.pub";
      68. String priPath = path + "/rsa.pri";
      69. File file = new File(path);
      70. if (!file.exists()) {
      71. file.mkdirs();
      72. } else {
      73. File pubFile = new File(pubPath);
      74. File priFile = new File(priPath);
      75. if (!pubFile.exists() || !priFile.exists()) {
      76. pubFile.delete();
      77. priFile.delete();
      78. } else {
      79. flag = true;
      80. }
      81. }
      82. if (!flag) {
      83. RsaUtils.generateKey(pubPath, priPath, Contants.JWT_SECRET);
      84. }
      85. if (publicKey == null) {
      86. publicKey = RsaUtils.getPublicKey(pubPath);
      87. privateKey = RsaUtils.getPrivateKey(priPath);
      88. }
      89. }
      90. /**
      91. * 排序规则
      92. *
      93. * @return
      94. */
      95. @Override
      96. public int getOrder() {
      97. return -100;
      98. }
      99. /**
      100. * jwt全局过滤器
      101. *
      102. * @param exchange
      103. * @param chain
      104. * @return
      105. */
      106. @Override
      107. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      108. // 获取request和response
      109. ServerHttpRequest request = exchange.getRequest();
      110. ServerHttpResponse response = exchange.getResponse();
      111. // 获取请求的url
      112. String url = exchange.getRequest().getURI().getPath();
      113. // 检查url放行
      114. for (String skip : jwtProperties.getSkipAuthUrls()) {
      115. if (antPathMatcher.match(skip, url)) {
      116. return chain.filter(exchange);
      117. }
      118. }
      119. // 检查header中jwt是否存在
      120. MultiValueMap headers = request.getHeaders();
      121. if (CollectionUtils.isEmpty(headers) || !headers.containsKey(Contants.JWT_HEADER_KEY)) {
      122. return unAuthorized(exchange);
      123. }
      124. // 解析jwt
      125. String username = null;
      126. Map mapInfo = null;
      127. try {
      128. mapInfo = JwtUtils.getInfoFromToken(headers.getFirst(Contants.JWT_HEADER_KEY), publicKey);
      129. if (mapInfo == null || mapInfo.get("username") == null) {
      130. return unAuthorized(exchange);
      131. }
      132. username = mapInfo.get("username").toString();
      133. if (username.equals("")) {
      134. return unAuthorized(exchange);
      135. }
      136. } catch (Exception e) {
      137. return unAuthorized(exchange);
      138. }
      139. // 获取权限信息
      140. AuthInfo authInfo = null;
      141. String rKey = String.format(Contants.RKEY_SYSTEM_AUTHINFO, username);
      142. ValueOperations ops = stringRedisTemplate.opsForValue();
      143. String rValue = ops.get(rKey);
      144. if (rValue == null) {
      145. HttpResult result = systemFeignService.getAuthInfo(username);
      146. if (result.getCode() != HttpCode.SUCCESS || result.getData() == null) {
      147. return unAuthorized(exchange);
      148. } else {
      149. authInfo = JSONUtil.toBean(result.getData().toString(), AuthInfo.class);
      150. }
      151. } else {
      152. authInfo = JSONUtil.toBean(rValue, AuthInfo.class);
      153. }
      154. if (authInfo.getUserInfo() == null) {
      155. return unAuthorized(exchange);
      156. }
      157. // 密码校验
      158. if (!PasswordUtils.matchPassword(
      159. mapInfo.get("password").toString(),
      160. authInfo.getUserInfo().getPassword(),
      161. authInfo.getUserInfo().getSalt())) {
      162. return passwordError(exchange);
      163. }
      164. //判断账号是否可用
      165. if (authInfo.getUserInfo().getUserStatus()==0){
      166. return userNoUse(exchange);
      167. }
      168. String companyName = (String)mapInfo.get("companyName");
      169. List list = authInfo.getList();
      170. //获取用户在当前公司的所有菜单权限
      171. List collect = list.stream().filter(g -> g.getCompanyName().equals(companyName)).flatMap((a) -> a.getRoles().stream().flatMap(b -> b.getAuthorities().stream()).distinct()).distinct().collect(Collectors.toList());
      172. // 权限校验
      173. if (!username.equals("system")){
      174. if (collect.contains(url)) {
      175. return hasNoPermission(exchange);
      176. }
      177. }
      178. // 将认证信息放入header
      179. ServerHttpRequest host = exchange.getRequest().mutate().header(Contants.JWT_MAP_KEY, JSONUtil.toJsonStr(mapInfo)).build();
      180. ServerWebExchange build = exchange.mutate().request(host).build();
      181. return chain.filter(build);
      182. }
      183. private static Mono unAuthorized(ServerWebExchange exchange) {
      184. ServerHttpResponse response = exchange.getResponse();
      185. response.setStatusCode(HttpStatus.OK);
      186. response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
      187. HttpResult result = new HttpResult().builder().code(HttpCode.UNAUTHORIZED).build();
      188. return Mono.defer(() -> {
      189. byte[] bytes;
      190. try {
      191. bytes = new ObjectMapper().writeValueAsBytes(result);
      192. } catch (Exception e) {
      193. throw new RuntimeException();
      194. }
      195. DataBuffer buffer = response.bufferFactory().wrap(bytes);
      196. return response.writeWith(Flux.just(buffer));
      197. });
      198. }
      199. private static Mono passwordError(ServerWebExchange exchange) {
      200. ServerHttpResponse response = exchange.getResponse();
      201. response.setStatusCode(HttpStatus.OK);
      202. response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
      203. HttpResult result = new HttpResult().builder().code(HttpCode.PASSWORDERROR).message("密码错误").build();
      204. return Mono.defer(() -> {
      205. byte[] bytes;
      206. try {
      207. bytes = new ObjectMapper().writeValueAsBytes(result);
      208. } catch (Exception e) {
      209. throw new RuntimeException();
      210. }
      211. DataBuffer buffer = response.bufferFactory().wrap(bytes);
      212. return response.writeWith(Flux.just(buffer));
      213. });
      214. }
      215. private static Mono userNoUse(ServerWebExchange exchange) {
      216. ServerHttpResponse response = exchange.getResponse();
      217. response.setStatusCode(HttpStatus.OK);
      218. response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
      219. HttpResult result = new HttpResult().builder().code(HttpCode.USERNOUSE).message("账号不可用,请联系管理员").build();
      220. return Mono.defer(() -> {
      221. byte[] bytes;
      222. try {
      223. bytes = new ObjectMapper().writeValueAsBytes(result);
      224. } catch (Exception e) {
      225. throw new RuntimeException();
      226. }
      227. DataBuffer buffer = response.bufferFactory().wrap(bytes);
      228. return response.writeWith(Flux.just(buffer));
      229. });
      230. }
      231. private static Mono hasNoPermission(ServerWebExchange exchange) {
      232. ServerHttpResponse response = exchange.getResponse();
      233. response.setStatusCode(HttpStatus.OK);
      234. response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
      235. HttpResult result = new HttpResult().builder().code(HttpCode.FORBIDDEN).build();
      236. return Mono.defer(() -> {
      237. byte[] bytes;
      238. try {
      239. bytes = new ObjectMapper().writeValueAsBytes(result);
      240. } catch (Exception e) {
      241. throw new RuntimeException();
      242. }
      243. DataBuffer buffer = response.bufferFactory().wrap(bytes);
      244. return response.writeWith(Flux.just(buffer));
      245. });
      246. }
      247. }

      6.定义全局拦截器,再自定义一个注解,进行拦截判断,如果加了该注解,则说明该接口需要用到认证信息,则将认证信息从请求头中取出存入threadlocal中,涉及到两个类,一个是拦截器,一个是存储类,如下

      1. package sca.pro.system.common.request;
      2. import cn.hutool.json.JSONUtil;
      3. import org.springframework.web.method.HandlerMethod;
      4. import org.springframework.web.servlet.ModelAndView;
      5. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
      6. import sca.pro.common.contants.Contants;
      7. import sca.pro.common.jwt.MapInfo;
      8. import sca.pro.common.response.HttpCode;
      9. import sca.pro.common.response.HttpResult;
      10. import javax.servlet.http.HttpServletRequest;
      11. import javax.servlet.http.HttpServletResponse;
      12. import java.io.IOException;
      13. import java.io.PrintWriter;
      14. import java.lang.reflect.Method;
      15. /**
      16. * @author shq
      17. * @description Request拦截器
      18. * @createDate 2022-5-27
      19. * @updateUser
      20. * @updateDate
      21. * @updateRemark
      22. */
      23. public class RequestContextInterceptor extends HandlerInterceptorAdapter {
      24. @Override
      25. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      26. if (!(handler instanceof HandlerMethod)) {
      27. return true;
      28. }
      29. Method method = ((HandlerMethod) handler).getMethod();
      30. if (!method.isAnnotationPresent(AuthInfoRequired.class)) {
      31. return true;
      32. }
      33. AuthInfoRequired annotation = method.getAnnotation(AuthInfoRequired.class);
      34. if (!annotation.required()) {
      35. return true;
      36. }
      37. if (initHeaderContext(request)) {
      38. return super.preHandle(request, response, handler);
      39. } else {
      40. returnJson(response, JSONUtil.toJsonStr(new HttpResult().builder()
      41. .code(HttpCode.UNAUTHORIZED).build()));
      42. return false;
      43. }
      44. }
      45. private boolean initHeaderContext(HttpServletRequest request) {
      46. String mapInfoStr = request.getHeader(Contants.JWT_MAP_KEY);
      47. if (mapInfoStr != null) {
      48. try {
      49. MapInfo mapInfo = JSONUtil.toBean(mapInfoStr, MapInfo.class);
      50. new RequestContext.RequestContextBuild()
      51. .mapInfo(mapInfo)
      52. .bulid();
      53. if (mapInfo.getUsername() == null) {
      54. return false;
      55. }
      56. return true;
      57. } catch (Exception e) {
      58. return false;
      59. }
      60. } else {
      61. return false;
      62. }
      63. }
      64. private void returnJson(HttpServletResponse response, String json) throws Exception {
      65. PrintWriter writer = null;
      66. response.setCharacterEncoding("UTF-8");
      67. response.setContentType("text/html; charset=utf-8");
      68. try {
      69. writer = response.getWriter();
      70. writer.print(json);
      71. } catch (IOException e) {
      72. } finally {
      73. if (writer != null)
      74. writer.close();
      75. }
      76. }
      77. @Override
      78. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
      79. RequestContext.clean();
      80. super.postHandle(request, response, handler, modelAndView);
      81. }
      82. }
      1. package sca.pro.system.common.request;
      2. import sca.pro.common.jwt.MapInfo;
      3. /**
      4. * @author shq
      5. * @description 请求上下文声明
      6. * @createDate 2022-5-27
      7. * @updateUser
      8. * @updateDate
      9. * @updateRemark
      10. */
      11. public class RequestContext {
      12. private static final ThreadLocal REQUEST_HEADER_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
      13. private MapInfo mapInfo;
      14. public String getUsername() {
      15. if (mapInfo != null) {
      16. return mapInfo.getUsername();
      17. }
      18. return null;
      19. }
      20. public String getCompanyName() {
      21. if (mapInfo != null) {
      22. return mapInfo.getCompanyName();
      23. }
      24. return null;
      25. }
      26. public String getlanguageName() {
      27. if (mapInfo != null) {
      28. return mapInfo.getLanguageName();
      29. }
      30. return null;
      31. }
      32. public static RequestContext getInstance() {
      33. return REQUEST_HEADER_CONTEXT_THREAD_LOCAL.get();
      34. }
      35. public void setContext(RequestContext context) {
      36. REQUEST_HEADER_CONTEXT_THREAD_LOCAL.set(context);
      37. }
      38. public static void clean() {
      39. REQUEST_HEADER_CONTEXT_THREAD_LOCAL.remove();
      40. }
      41. private RequestContext(RequestContextBuild requestHeaderContextBuild) {
      42. this.mapInfo = requestHeaderContextBuild.mapInfo;
      43. setContext(this);
      44. }
      45. public static class RequestContextBuild {
      46. private MapInfo mapInfo;
      47. public RequestContextBuild mapInfo(MapInfo mapInfo) {
      48. this.mapInfo = mapInfo;
      49. return this;
      50. }
      51. public RequestContext bulid() {
      52. return new RequestContext(this);
      53. }
      54. }
      55. }

      7.在需要使用认证信息的地方只需要加上这个AuthInfoRequired注解,即可通过RequestContext.getinstance().get对应的认证信息即可获取,到此第一种方法结束

      (2)为了保证网关的业务纯净,我们一般不在网管进行鉴权,而且上述的方法也稍微有些繁琐,我们可以自定义一个注解,通过aop环绕进行统一拦截,这次设计需要在nacos中定义一个独立的白名单文件,然后还是一样先对拦截的请求进行 白名单放行,然后获取token进行解析,获取对应的用户信息进行判读密码,权限校验,与上边相同,直接上代码‘

      1. package sca.pro.core.authentication.handler;
      2. import cn.hutool.core.convert.Convert;
      3. import cn.hutool.json.JSONUtil;
      4. import lombok.extern.log4j.Log4j2;
      5. import org.apache.commons.lang3.StringUtils;
      6. import org.aspectj.lang.ProceedingJoinPoint;
      7. import org.aspectj.lang.Signature;
      8. import org.aspectj.lang.annotation.Around;
      9. import org.aspectj.lang.annotation.Aspect;
      10. import org.aspectj.lang.annotation.Pointcut;
      11. import org.aspectj.lang.reflect.MethodSignature;
      12. import org.springframework.beans.factory.annotation.Autowired;
      13. import org.springframework.beans.factory.annotation.Value;
      14. import org.springframework.boot.context.properties.EnableConfigurationProperties;
      15. import org.springframework.core.annotation.Order;
      16. import org.springframework.data.redis.core.StringRedisTemplate;
      17. import org.springframework.data.redis.core.ValueOperations;
      18. import org.springframework.stereotype.Component;
      19. import org.springframework.util.AntPathMatcher;
      20. import org.springframework.web.context.request.RequestContextHolder;
      21. import org.springframework.web.context.request.ServletRequestAttributes;
      22. import sca.pro.common.contants.Contants;
      23. import sca.pro.common.exception.BusinessException;
      24. import sca.pro.common.jwt.MapInfo;
      25. import sca.pro.common.redis.authInfo.AuthInfo;
      26. import sca.pro.common.redis.authInfo.CompanyInfo;
      27. import sca.pro.common.response.HttpCode;
      28. import sca.pro.common.response.HttpResult;
      29. import sca.pro.common.threadlocal.ThreadLocalUtils;
      30. import sca.pro.common.utils.PasswordUtils;
      31. import sca.pro.common.utils.SystemUtils;
      32. import sca.pro.core.authentication.annotation.HasPermission;
      33. import sca.pro.core.authentication.util.JwtProperties;
      34. import sca.pro.core.authentication.util.JwtUtils;
      35. import sca.pro.core.authentication.util.RsaUtils;
      36. import sca.pro.core.feign.SystemCheckFeignService;
      37. import javax.annotation.PostConstruct;
      38. import javax.annotation.Resource;
      39. import javax.servlet.http.HttpServletRequest;
      40. import java.io.File;
      41. import java.io.UnsupportedEncodingException;
      42. import java.security.PrivateKey;
      43. import java.security.PublicKey;
      44. import java.util.*;
      45. import java.util.stream.Collectors;
      46. @Log4j2
      47. @Aspect
      48. // 数字越小,执行顺序越高,@Transactional的顺序为Integer.MAX_VALUE
      49. @Order(-1)
      50. @Component
      51. @EnableConfigurationProperties(JwtProperties.class)
      52. public class AuthenticationHandler {
      53. // spring的路径匹配器
      54. private final static AntPathMatcher antPathMatcher = new AntPathMatcher();
      55. private static PublicKey publicKey;
      56. private static PrivateKey privateKey;
      57. @Resource
      58. private JwtProperties jwtProperties;
      59. @Autowired
      60. private StringRedisTemplate stringRedisTemplate;
      61. @Autowired
      62. private SystemCheckFeignService systemFeignService;
      63. @Value("${spring.application.name}")
      64. private String springApplicationName;
      65. /**
      66. * 切入点
      67. * 1. execution 表达式主体
      68. * 2. * 任意类型返回值
      69. * 3. com.company.web.controller 切入包
      70. * 4. .. 当前包及子包
      71. * 5. * 所有类
      72. * 6. .*(..) 所有方法与任何参数
      73. */
      74. @Pointcut("execution(* sca.pro.*.controller..*.*(..)))")
      75. public void cut() {
      76. }
      77. /**
      78. * 本执行在事务外层,在GlobalExceptionHandler内层
      79. *
      80. * @param joinPoint
      81. * @throws Throwable
      82. */
      83. @Around("cut()")
      84. public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
      85. long startTime = System.currentTimeMillis();
      86. ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      87. HttpServletRequest request = sra.getRequest();
      88. Signature signature = joinPoint.getSignature();
      89. MethodSignature methodSignature = (MethodSignature) signature;
      90. log.info("Request begin");
      91. log.info("ModuleName: {}", springApplicationName);
      92. log.info("RequestMethod: {}", request.getMethod());
      93. log.info("RequestURI: {}", request.getRequestURI());
      94. log.info("RemoteAddr: {}", request.getRemoteAddr());
      95. log.info("MethodName: {}", methodSignature.getDeclaringTypeName() + "." + methodSignature.getName());
      96. // todo 入参待处理
      97. // Object[] args = joinPoint.getArgs();
      98. // 用户身份认证
      99. // 网关强制过滤掉了GlobalContants.HEADER_LOGIN_INFO_BASE64_KEY,在内部进行构建
      100. boolean isLoginInfoRequired = methodSignature.getMethod().isAnnotationPresent(HasPermission.class);
      101. try {
      102. if (isLoginInfoRequired) {
      103. String loginInfoStr = sra.getRequest().getHeader(Contants.HEADER_LOGIN_INFO_BASE64_KEY);
      104. if (loginInfoStr != null) {
      105. // 将请求头信息加入本地变量,用于跨服务调用 将请求头信息进行解析,用于业务处理
      106. ThreadLocalUtils.set(Contants.HEADER_LOGIN_INFO_BASE64_KEY, loginInfoStr);
      107. MapInfo mapInfo=JSONUtil.toBean(JSONUtil.parseObj(loginInfoStr), MapInfo.class);
      108. ThreadLocalUtils.set(Contants.LOCAL_LOGIN_INFO_OBJECT_KEY, mapInfo);
      109. } else {
      110. // 获取request和response
      111. // 获取请求的url
      112. String url =request.getRequestURI() ;
      113. // 检查url白名单放行
      114. for (String skip : jwtProperties.getSkipAuthUrls()) {
      115. if (antPathMatcher.match(skip, url)) {
      116. Object object = joinPoint.proceed(joinPoint.getArgs());
      117. // todo 出参待处理
      118. return object;
      119. }
      120. }
      121. Map mapInfo = null;
      122. AuthInfo authInfo = null;
      123. // 检查header中jwt是否存在
      124. String header = request.getHeader(Contants.JWT_HEADER_KEY);
      125. if (StringUtils.isEmpty(header)) {
      126. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      127. }
      128. // 解析jwt
      129. String username = null;
      130. try {
      131. mapInfo = JwtUtils.getInfoFromToken(URLDecoderString(header), publicKey);
      132. if (mapInfo == null || mapInfo.get("username") == null) {
      133. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      134. }
      135. username = mapInfo.get("username").toString();
      136. if (username.equals("")) {
      137. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      138. }
      139. //判断token是否是最新的
      140. String token = systemFeignService.getToken(username);
      141. if (token!=null){
      142. if (!token.equals(header)){
      143. throw new BusinessException(7777, "账号已在别处登录");
      144. }
      145. }
      146. } catch (Exception e) {
      147. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      148. }
      149. // 获取权限信息
      150. String rKey = String.format(Contants.RKEY_SYSTEM_AUTHINFO, username);
      151. rKey = StringUtils.join(rKey, ":", username);
      152. ValueOperations ops = stringRedisTemplate.opsForValue();
      153. String rValue = ops.get(rKey);
      154. if (rValue == null) {
      155. HttpResult result = systemFeignService.getAuthInfo(username, mapInfo.get("companyName").toString());
      156. if (result.getCode() != HttpCode.SUCCESS || result.getData() == null) {
      157. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      158. } else {
      159. authInfo = JSONUtil.toBean(result.getData().toString(), AuthInfo.class);
      160. }
      161. } else {
      162. authInfo = JSONUtil.toBean(rValue, AuthInfo.class);
      163. }
      164. if (authInfo.getUserInfo() == null) {
      165. throw new BusinessException(HttpCode.UNAUTHORIZED, "未授权");
      166. }
      167. // 密码校验
      168. if (!PasswordUtils.matchPassword(
      169. mapInfo.get("password").toString(),
      170. authInfo.getUserInfo().getPassword(),
      171. authInfo.getUserInfo().getSalt())) {
      172. throw new BusinessException(HttpCode.PASSWORDERROR, "密码错误");
      173. }
      174. //判断账号是否可用
      175. if (authInfo.getUserInfo().getUserStatus() == 0) {
      176. throw new BusinessException(HttpCode.USERNOUSE, "账号不可用请联系管理员");
      177. }
      178. String companyName = (String) mapInfo.get("companyName");
      179. List list = authInfo.getList();
      180. //获取用户在当前公司的所有菜单权限
      181. List collect = list.stream().filter(g -> g.getCompanyName().equals(companyName)).flatMap((a) -> a.getRoles().stream().flatMap(b -> b.getAuthorities().stream()).distinct()).distinct().collect(Collectors.toList());
      182. // 权限校验
      183. if (!username.equals("system")) {
      184. if (collect.contains(url)) {
      185. throw new BusinessException(HttpCode.FORBIDDEN, "无权限");
      186. }
      187. }
      188. // 将请求头信息加入本地变量,用于跨服务调用 将请求头信息进行解析,用于业务处理
      189. ThreadLocalUtils.set(Contants.HEADER_LOGIN_INFO_BASE64_KEY,JSONUtil.toJsonStr( Convert.convert(MapInfo.class,mapInfo)));
      190. MapInfo loginInfo =Convert.convert(MapInfo.class,mapInfo);
      191. ThreadLocalUtils.set(Contants.LOCAL_LOGIN_INFO_OBJECT_KEY, loginInfo);
      192. }
      193. }
      194. Object object = joinPoint.proceed(joinPoint.getArgs());
      195. // todo 出参待处理
      196. return object;
      197. } catch (Throwable e) {
      198. throw e;
      199. } finally {
      200. log.info("Cost time: {}ms", System.currentTimeMillis() - startTime);
      201. ThreadLocalUtils.clear();
      202. }
      203. }
      204. public static String URLDecoderString(String str) {
      205. String result = "";
      206. if (null == str) {
      207. return "";
      208. }
      209. try {
      210. result = java.net.URLDecoder.decode(str, "UTF-8");
      211. } catch (UnsupportedEncodingException e) {
      212. e.printStackTrace();
      213. }
      214. return result;
      215. }
      216. /**
      217. * 初始化公私钥
      218. *
      219. * @throws Exception
      220. */
      221. @PostConstruct
      222. public void init() throws Exception {
      223. boolean flag = false;
      224. String path = SystemUtils.getApplicationPath() + Contants.JWT_RSAKEY_PATH;
      225. String pubPath = path + "/rsa.pub";
      226. String priPath = path + "/rsa.pri";
      227. File file = new File(path);
      228. if (!file.exists()) {
      229. file.mkdirs();
      230. } else {
      231. File pubFile = new File(pubPath);
      232. File priFile = new File(priPath);
      233. if (!pubFile.exists() || !priFile.exists()) {
      234. pubFile.delete();
      235. priFile.delete();
      236. } else {
      237. flag = true;
      238. }
      239. }
      240. if (!flag) {
      241. RsaUtils.generateKey(pubPath, priPath, Contants.JWT_SECRET);
      242. }
      243. if (publicKey == null) {
      244. publicKey = RsaUtils.getPublicKey(pubPath);
      245. privateKey = RsaUtils.getPrivateKey(priPath);
      246. }
      247. }
      248. }

    23. 相关阅读:
      C++编程常见错误及处理
      编辑“事件处理”中设置鼠标指针样式
      基于SpERT的中文关系抽取
      DS5上ARM编译器样例工程改为GCC编译
      C语言第三十一弹---自定义类型:结构体(下)
      校园论坛(Java)—— 数据报表模块
      HTML 之 块级元素、行内元素和行内块元素之间的嵌套规则
      软件测试面试题:请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系?
      「实用场景教程」如何用日程控件DHTMLX Scheduler制作酒店预订日历?(二)
      git命令 本地
    24. 原文地址:https://blog.csdn.net/weixin_59244784/article/details/127106362