拦截器配置与注册

本文详细介绍了在SpringBoot中如何使用拦截器实现前端请求的拦截,防止恶意模拟请求,并在后台进行鉴权处理。通过在拦截器中检查用户的登录状态和Token,确保用户信息的安全。同时,当用户信息过期或在不同设备上登录时,系统会返回相应的错误提示,要求用户重新登录。此外,文章还展示了前端如何在请求头中添加必要的用户信息,以及当拦截器返回错误状态时,前端如何处理并跳转到登录页面。
摘要由CSDN通过智能技术生成

一、前端拦截的缺陷

见链接:https://blog.csdn.net/Tom870223050/article/details/115977961?spm=1001.2014.3001.5502
只有前端拦截,可能会被恶意地进行模拟请求,也就是绕过前端,直接访问后台,所以我们在后台还需要配置拦截器

二、springBoot拦截器

springMvc和springBoot实现拦截器方法差不多,但是 springMvc的拦截器是在xml里面注册的,springBoot是省略了xml的配置,直接编写一个实现类进行注册。

  1. 引入MiniInterceptor

重要类:
1.preHandle:拦截请求,在controller调用之前
2.postHandle:请求controller之后,渲染视图之前
3.afterCompletion: 请求controller之后,视图渲染之后

package com.imooc.controller.interceptor;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.imooc.utils.IMoocJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;

public class MiniInterceptor implements HandlerInterceptor {

	@Autowired
	public RedisOperator redis;
	public static final String USER_REDIS_SESSION = "user-redis-session";
	
	/**
	 * 拦截请求,在controller调用之前
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
			Object arg2) throws Exception {
		
		/**
		 * 返回 false:请求被拦截,返回
		 * 返回 true :请求OK,可以继续执行,放行
		 */
		return true;
	}
	
	public void returnErrorResponse(HttpServletResponse response, IMoocJSONResult result) 
			throws IOException, UnsupportedEncodingException {
		
	}
	
	/**
	 * 请求controller之后,渲染视图之前
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}
	
	/**
	 * 请求controller之后,视图渲染之后
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

}

2 . 注册拦截器类

这时我们需要把MiniInterceptor类注册在WebMvcConfig类中

  • webMvcConfig.java

(1)首先我们需要把它注册在spring中,为一个Bean

	@Bean
	public MiniInterceptor miniInterceptor() {
		return new MiniInterceptor();
	}

(2)然后我们把它注册在拦截器中。
addPathPatterns()表示添加需要进行拦截的路径,也就是我们的后台接口,**表示对其中的所有方法都要进行拦截

@Override
	public void addInterceptors(InterceptorRegistry registry) {
		
		registry.addInterceptor(miniInterceptor()).addPathPatterns("/user/**")
				       .addPathPatterns("/video/upload", "/video/uploadCover",
				    		   			"/video/userLike", "/video/userUnLike",
				    		   			"/video/saveComment")
												  .addPathPatterns("/bgm/**")
												  .excludePathPatterns("/user/queryPublisher");
		
		super.addInterceptors(registry);
	}
  1. 编写拦截器

用户在登陆成功即可保存用户id和TokenToken可以放在redis里面

//loginController.java

 public UsersVO setUserRedisSessionToken(Users userModel){
	   String uniqueToken = UUID.randomUUID().toString();
	   redis.set(USER_REDIS_SESSION+":"+userModel.getId(),uniqueToken,1000*60*30);

	   UsersVO usersVO = new UsersVO();
	   BeanUtils.copyProperties(userModel,usersVO);
	   usersVO.setUserToken(uniqueToken);
	   return usersVO;
   }

后台在进行鉴权的时候要用到存放用户信息的Token和用户id,所以前端向后台发送信息的时候要加上这些信息

//mine.js

 url:
 method: "POST",
      header: {
        'content-type': 'application/json', // 默认值
        'headerUserId': user.id,
        'headerUserToken': user.userToken
      }

//MiniInterceptor

package com.imooc.controller.interceptor;

import com.imooc.utils.IMoocJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

public class MiniInterceptor implements HandlerInterceptor {

	@Autowired
	public RedisOperator redis;
	public static final String USER_REDIS_SESSION = "user-redis-session";
	
	/**
	 * 拦截请求,在controller调用之前
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
			Object arg2) throws Exception {

		String userId = request.getHeader("headerUserId");
		String userToken = request.getHeader("headerUserToken");
		
		if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
			String uniqueToken = redis.get(USER_REDIS_SESSION + ":" + userId);
			if (StringUtils.isEmpty(uniqueToken) && StringUtils.isBlank(uniqueToken)) {
				System.out.println("用户信息过期,请重新登录...");
				returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("请登录..."));
				return false;
			} else {
				if (!uniqueToken.equals(userToken)) {
					System.out.println("账号被挤出...");
					returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("账号被挤出..."));
					return false;
				}
			}
		} else {
			System.out.println("请登录...");
			returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("请登录..."));
			return false;
		}
		
		
		/**
		 * 返回 false:请求被拦截,返回
		 * 返回 true :请求OK,可以继续执行,放行
		 */
		return true;
	}
	
	public void returnErrorResponse(HttpServletResponse response, IMoocJSONResult result) 
			throws IOException, UnsupportedEncodingException {
		OutputStream out=null;
		try{
		    response.setCharacterEncoding("utf-8");
		    response.setContentType("text/json");
		    out = response.getOutputStream();
		    out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
		    out.flush();
		} finally{
		    if(out!=null){
		        out.close();
		    }
		}
	}
	
	/**
	 * 请求controller之后,渲染视图之前
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	}
	
	/**
	 * 请求controller之后,视图渲染之后
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
	}

}

preHandle方法:首先获取到前端的Header发送字段,然后进行判空,成功后若token到达时间了会失效,此时后台返回重新登陆信息,还有一种情况是用户在一个手机上进行登陆,又在第二台手机上进行登陆,这时会向后台发送两次信息,其中,两次的userToken信息是不一样的,第一次发送的userToken保存到变量uniqueToken里,当!uniqueToken.equals(userToken)为真表示用户在两台手机上进行登陆,在这个业务逻辑里我们视为不安全登陆,之前的登陆状态将会被挤出

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
			Object arg2) throws Exception {

		String userId = request.getHeader("headerUserId");
		String userToken = request.getHeader("headerUserToken");      // 获取到前端的Header发送字段
		
		if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
			String uniqueToken = redis.get(USER_REDIS_SESSION + ":" + userId);
			if (StringUtils.isEmpty(uniqueToken) && StringUtils.isBlank(uniqueToken)) {
				System.out.println("用户信息过期,请重新登录...");
				returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("请登  录..."));                                                              
				return false;
			} else {
				if (!uniqueToken.equals(userToken)) {                
					System.out.println("账号被挤出...");
					returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("账号被挤出..."));                                  //后一次的用户信息和前一次的不一致
					return false;
				}
			}
		} else {
			System.out.println("请登录...");
			returnErrorResponse(response, new IMoocJSONResult().errorTokenMsg("请登录..."));
			return false;
		}
		
		
		/**
		 * 返回 false:请求被拦截,返回
		 * 返回 true :请求OK,可以继续执行,放行
		 */
		return true;
	}

4.处理拦截信息

//MiniInterceptor.java

returnErrorResponse方法:设置拦截器返回前端的errorTokenMsg信息格式

@Override
public void returnErrorResponse(HttpServletResponse response, IMoocJSONResult result) 
			throws IOException, UnsupportedEncodingException {
		OutputStream out=null;
		try{
		    response.setCharacterEncoding("utf-8");
		    response.setContentType("text/json");
		    out = response.getOutputStream();
		    out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
		    out.flush();
		} finally{
		    if(out!=null){
		        out.close();
		    }
		}
	}

//XX.js

我们设置被拦截后,后端发送的状态码为502,前端接受错误信息并跳转到登陆页面

if (res.data.status == 502) {
          wx.showToast({
            title: res.data.msg,
            duration: 3000,
            icon: "none",
            success: function () {
              wx.redirectTo({
                url: '../userLogin/login',
              })
            }
          })
        }

5.完整的前端请求如下

wx.request({
      url: serverUrl + '/XXX?userId=' + userId,
      method: "POST",
      header: {
        'content-type': 'application/json', // 默认值
        'headerUserId': user.id,
        'headerUserToken': user.userToken
      },
      success: function (res) {
        wx.hideLoading();
        if (res.data.status == 200) {
           //登陆成功的操作
          }

        } else if (res.data.status == 502) {
          wx.showToast({
            title: res.data.msg,
            duration: 3000,
            icon: "none",
            success: function () {
              wx.redirectTo({
                url: '../userLogin/login',
                //Token失效后跳转到登陆界面
              })
            }
          })
        }
      }
    })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值