Spring Boot 拦截器配置拦截登陆

一,pom.xml 的配置

这里很简单,先引入 spring-boot-starter-parent,parent 是父模块,由父模块统一进行 spring-boot 版本管理,dependencies 中与 spring-boot 启动绑定的包不需要再指定版本。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>
     
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

二,新建 WebConfigurer

1,新建的 config 包,用来装初始化文件,在配置之下新建 WebConfigurer。

2,WebConfigurer 需要实现 WebMvcConfigurer 这个接口,并实现里面的两个方法。(在老版本的 spring-boot 中使用的是 WebMvcConfigurerAdapter,新版本中已过时!!!还有不能通过继承 WebMvcConfigurationSupport 这个类来实现,这样会在某些情况下失效!!!),第二个 addInterceptors 方法用来注册添加拦截器。

import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  
 @Configuration
 public class WebConfigurer implements WebMvcConfigurer {
  
   // 这个方法是用来配置静态资源的,比如html,js,css,等等
   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
   }
  
   // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
   }
 }

三,新建登陆拦截器

在 config 包下新建一个 intercepors 包,用来装拦截器。然后在拦截器下新建 LoginInterceptor,用来拦截验证登陆。
每一个拦截器有需要实现的 HandlerInterceptor 接口,这个接口有三个方法,每个方法会在请求调用的不同时期完成,因为我们需要在接口调用之前拦截请求判断是否登陆,所以这里需要使用 preHandle 方法,在里面写验证逻辑,最后返回 true 或者 false,确定请求是否合法。记住加 @Component 注解,我们需要在上一步的 WebConfigurer 类中注入。用户是自定义的用户类,大家可以自己定义,我这里就不贴出来了。

import com.impte.study.domain.po.User;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
  
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
  
 @Component
 public class LoginInterceptor implements HandlerInterceptor {
  
     //这个方法是在访问接口之前执行的,我们只需要在这里写验证登陆状态的业务逻辑,就可以在用户调用指定接口之前验证登陆状态了
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         //每一个项目对于登陆的实现逻辑都有所区别,我这里使用最简单的Session提取User来验证登陆。
         HttpSession session = request.getSession();
         //这里的User是登陆时放入session的
         User user = (User) session.getAttribute("user");
         //如果session中没有user,表示没登陆
         if (user == null){
             //这个方法返回false表示忽略当前请求,如果一个用户调用了需要登陆才能使用的接口,如果他没有登陆这里会直接忽略掉
             //当然你可以利用response给用户返回一些提示信息,告诉他没登陆
             return false;
         }else {
             return true;    //如果session里有user,表示该用户已经登陆,放行,用户即可继续调用自己需要的接口
         }
     }
  
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
     }
  
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
     }
 }

四,在 WebConfigurer 中添加拦截器

1,首先将 LoginInterceptor 注入到 WebConfigurer 中。

        @Autowired
        private LoginInterceptor loginInterceptor;

2,然后在 WebConfigurer 中的 addInterceptors 中添加拦截器,使其生效。

        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // addPathPatterns("/**") 表示拦截所有的请求,
            // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
            registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/register");
            super.addInterceptors(registry);    //较新Spring Boot的版本中这里可以直接去掉,否则会报错
        }

3,addPathPatterns 用来设置拦截路径,excludePathPatterns 用来设置白名单,也就是不需要触发这个拦截器的路径。

完整示例:

WebConfigurer.java

package com.cmiot.airwrite.config;

import com.cmiot.airwrite.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;

import java.util.List;

/**
* Description
*
* @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
* @Date: 2019/6/19 16:17
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
 @Autowired
 private LoginInterceptor loginInterceptor;


 @Override
 public void configurePathMatch(PathMatchConfigurer configurer) {

 }

 @Override
 public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

 }

 @Override
 public void configureAsyncSupport(AsyncSupportConfigurer configurer) {

 }

 @Override
 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

 }

 @Override
 public void addFormatters(FormatterRegistry registry) {

 }
 // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
 /**
  * springboot2.0之前版本不会拦截static目录下的静态资源
  * springboot2.0之后的版本会拦截static目录下的静态资源,需要手动指定规则
  * */

 @Override
 public void addInterceptors(InterceptorRegistry registry) {
     //表示拦截所有的请求
     //addPathPatterns("/**");
     //表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
     //excludePathPatterns("/login", "/register");
     //2.0之前版本配置
     //registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/register");
	//2.0版本之后配置
     registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/register","/static/**");
 }
 // 这个方法是用来配置静态资源的,比如html,js,css,等等
 @Override
 public void addResourceHandlers(ResourceHandlerRegistry registry) {

 }

 @Override
 public void addCorsMappings(CorsRegistry registry) {

 }

 @Override
 public void addViewControllers(ViewControllerRegistry registry) {

 }

 @Override
 public void configureViewResolvers(ViewResolverRegistry registry) {

 }

 @Override
 public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {

 }

 @Override
 public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {

 }

 @Override
 public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

 }

 @Override
 public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

 }

 @Override
 public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {

 }

 @Override
 public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {

 }

 @Override
 public Validator getValidator() {
     return null;
 }

 @Override
 public MessageCodesResolver getMessageCodesResolver() {
     return null;
 }
}

LoginInterceptor.java

package com.cmiot.airwrite.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.cmiot.airwrite.constants.ConfigConstants;
import com.cmiot.airwrite.constants.ExceptionConstants;
import com.cmiot.airwrite.entity.User;
import com.cmiot.airwrite.util.HttpUtil;
import com.cmiot.airwrite.util.StringUtils;
import com.sun.istack.internal.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


/**
 * Description
 *
 * @author <a href="https://qiankunpingtai.cn">qiankunpingtai</a>
 * @Date: 2019/6/19 15:46
 */
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {
    /**
     * admin允许登录的ip
     */
    private static String[] ADMIN_ALLOW_IPS = {"127.0.0.1", "127.0.0.2", "192.168.218.184"};
    /**
     * admin用户只能在特定网段登录
     * */
    /**
     * 在请求处理之前进行调用(Controller方法调用之前)
     * 基于URL实现的拦截器
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //2.0版本之后,访问根目录,跳转到index.html
        String url = request.getServletPath().toString();
        if(url.endsWith("/")){
            response.sendRedirect("/index.html");
            return false;
        }
        int checkFlag=checkVisit(request);
        if(checkFlag>0){
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=utf-8");
            Map<String, Object> resultMap = new HashMap<String, Object>();
            String responsString=null;
            if(checkFlag==1){
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.LOGIN_PARAM_MISSING_CODE);
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.LOGIN_PARAM_MISSING_MSG);
                //缺少登录参数
            }else if(checkFlag==2){
                //当前用户只能在指定ip下登录
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.LOGIN_ONLY_FOR_ASSIGN_IPS_CODE);
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.LOGIN_ONLY_FOR_ASSIGN_IPS_MSG);
            }else if(checkFlag==3){
                //用户未登录
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.LOGIN_NOT_CODE);
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.LOGIN_NOT_MSG);
            }else if(checkFlag==4) {
                //当前用户只能在指定ip下访问
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.VISIT_ONLY_FOR_ASSIGN_IPS_CODE);
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.VISIT_ONLY_FOR_ASSIGN_IPS_MSG);
            }else{
                //未知异常
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_CODE,ExceptionConstants.SERVICE_SYSTEM_ERROR_CODE);
                resultMap.put(ExceptionConstants.GLOBAL_RETURNS_MESSAGE,ExceptionConstants.SERVICE_SYSTEM_ERROR_MSG);
            }
            resultMap.put(ExceptionConstants.GLOBAL_RETURNS_DATA,"");
            responsString= JSON.toJSONString(resultMap, SerializerFeature.WriteMapNullValue);
            response.getWriter().print(responsString);
            return false;
        }else{
            return true;
        }
    }
    private int checkVisit(HttpServletRequest request){
        User user= HttpUtil.getUser(request);
        if(user==null){
            /**
             * 如果访问的登录接口
             * 并且用户是admin,校验登录ip
             */
            String url=request.getRequestURI().toString();
            if(url.contains("/login")){
                //登录接口,获取登录参数
                String userJson=request.getParameter("userJson");
                if(StringUtils.isEmpty(userJson)){
                    return 1;
                }
                User loginUser = JSONObject.parseObject(userJson, User.class);
                if(loginUser!=null&&ConfigConstants.ADMIN_USER_NAME.equals(loginUser.getUserName())){
                    //获取登录的ip
                    String ip =HttpUtil.getIpAddr(request);
                    if(Arrays.asList(ADMIN_ALLOW_IPS).contains(ip)){
                        return 0;
                    }else{
                        return 2;
                    }
                }
                return 0;
            }
            //没有登录,无法访问
            return 3;
        }else{
            if(ConfigConstants.ADMIN_USER_NAME.equals(user.getUserName())){
                //获取登录的ip
                String ip =HttpUtil.getIpAddr(request);
                if(Arrays.asList(ADMIN_ALLOW_IPS).contains(ip)){
                    return 0;
                }else{
                    return 4;
                }
            }
            return 0;
        }
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }

}