SpringCloud如何通过配置文件实现动态登录拦截

1. 前言

通过配置文件实现动态登录拦截的好处有:

  1. 可以实现灵活的权限控制:通过在配置文件中配置需要拦截的URL,可以灵活地控制哪些URL需要进行登录拦截。不需要修改代码,只需要修改配置文件即可实现权限控制。

  2. 可以快速响应变化:由于配置文件可以实现动态加载,当需要修改登录拦截的URL时,只需要修改配置文件,然后重新加载即可,不需要重启应用,可以快速响应变化。

  3. 可以集中管理权限配置:通过在配置文件中集中管理登录拦截的URL,可以方便地管理各个微服务的权限配置。不需要在每个微服务中分别配置登录拦截的URL,只需要在配置中心统一管理即可。

  4. 可以实现微服务的解耦:通过使用配置文件,可以将登录拦截的配置和具体的微服务解耦。不需要在每个微服务中编写相同的登录拦截逻辑,只需要在配置中心进行配置即可。这样可以减少代码的重复性,提高开发效率。

  5. 可以方便地进行测试和部署:由于登录拦截的配置是通过配置文件进行管理的,可以方便地进行测试和部署。可以通过不同的配置文件设置不同的拦截规则,方便地进行测试和部署不同的环境。

2.  代码实现

2.1 导入依赖

<!--configuration-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>
<!--hutool工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.17</version>
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>

2.2  创建读取拦截规则的配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

@Data
@ConfigurationProperties(prefix = "demo.auth.resource")
public class ResourceAuthProperties {
    /**
     * 是否开启登录拦截功能,如果开启则需要指定拦截路径,默认拦截所有
     */
    private Boolean enable = false;
    /**
     * 要拦截的路径,例如:/user/**
     */
    private List<String> includeLoginPaths;
    /**
     * 不拦截的路径,例如:/user/**
     */
    private List<String> excludeLoginPaths;
}

2.3 创建存放 ThreadLocal 实体类

public class UserContext {
    private static final ThreadLocal<Long> TL = new ThreadLocal<>();

    /**
     * 保存用户信息
     * @param userId 用户id
     */
    public static void setUser(Long userId){
        TL.set(userId);
    }

    /**
     * 获取用户
     * @return 用户id
     */
    public static Long getUser(){
        return TL.get();
    }

    /**
     * 移除用户信息
     */
    public static void removeUser(){
        TL.remove();
    }
}

 2.4 创建拦截器拦截请求头中的用户信息并存放到 ThreadLocal 中

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class UserInfoInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.尝试获取头信息中的用户信息
        String authorization = request.getHeader("user-info");
        // 2.判断是否为空
        if (authorization == null) {
            return true;
        }
        // 3.转为用户id并保存
        try {
            Long userId = Long.valueOf(authorization);
            UserContext.setUser(userId);
            return true;
        } catch (NumberFormatException e) {
            log.error("用户身份信息格式不正确,{}, 原因:{}", authorization, e.getMessage());
            return true;
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清理用户信息
        UserContext.removeUser();
    }
}

 2.5 创建拦截器用于登录校验

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class LoginAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1.尝试获取用户信息
        Long userId = UserContext.getUser();
        // 2.判断是否登录
        if (userId == null) {
            response.setStatus(401);
            response.sendError(401, "未登录用户无法访问!");
            // 2.3.未登录,直接拦截
            return false;
        }
        // 3.登录则放行
        return true;
    }
}

2.6 根据配置类的规则注册两个拦截器

import cn.hutool.core.collection.CollUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableConfigurationProperties(ResourceAuthProperties.class)
public class ResourceInterceptorConfiguration implements WebMvcConfigurer {

    private final ResourceAuthProperties authProperties;

    @Autowired
    public ResourceInterceptorConfiguration(ResourceAuthProperties resourceAuthProperties) {
        this.authProperties = resourceAuthProperties;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 1.添加用户信息拦截器
        registry.addInterceptor(new UserInfoInterceptor()).order(0);
        // 2.是否需要做登录拦截
        if(!authProperties.getEnable()){
            // 无需登录拦截
            return;
        }
        // 2.添加登录拦截器
        InterceptorRegistration registration = registry.addInterceptor(new LoginAuthInterceptor()).order(1);
        // 2.1.添加拦截器路径
        if(CollUtil.isNotEmpty(authProperties.getIncludeLoginPaths())){
            registration.addPathPatterns(authProperties.getIncludeLoginPaths());
        }
        // 2.2.添加排除路径
        if(CollUtil.isNotEmpty(authProperties.getExcludeLoginPaths())){
            registration.excludePathPatterns(authProperties.getExcludeLoginPaths());
        }
        // 2.3.排除swagger路径
        registration.excludePathPatterns(
            "/v2/**",
            "/v3/**",
            "/swagger-resources/**",
            "/webjars/**",
            "/doc.html"
        );
    }
}

2.7 将 ResourceInterceptorConfiguration 类实现自动配置

方式一:在 resources 目录下创建 META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.demo.authsdk.resource.config.ResourceInterceptorConfiguration

方式二: 在 resources 目录下创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.demo.authsdk.resource.config.ResourceInterceptorConfiguration

2.8 8. 在 application.yml 添加规则

例如:

  • 除了 /user/login 其他都拦截
demo:
  auth:
    resource:
      # 是否拦截
      enable: true
      # 不拦截路径
      exclude-login-paths: /user/login
  •  除了  /user/login 其他都不拦截 
demo:
  auth:
    resource:
      # 是否拦截
      enable: false
      # 拦截的路径
      include-login-paths: /user/login