热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot实现登录拦截器超详细(springboot拦截器excludePathPatterns方法不生效的坑)

文章目录SpringBoot实现登录拦截器1、SpringBoot实现登录拦截的原理1.1、实现HandlerInterceptor接口1.2、实现WebMvcConfigurer

文章目录


  • SpringBoot实现登录拦截器
    • 1、SpringBoot 实现登录拦截的原理
      • 1.1、实现`HandlerInterceptor`接口
      • 1.2、实现`WebMvcConfigurer`接口,注册拦截器
      • 1.3、保持登录状态


  • springboot拦截器excludePathPatterns方法不生效的坑与解决方法
      • 一、前言
      • 二、问题
      • 三、解决方法
      • 四、总结
      • 五、扩展
        • "/user/login"
        • "/login"







SpringBoot实现登录拦截器

对于管理系统或其他需要用户登录的系统,登录验证都是必不可少的环节,在 SpringBoot 开发的项目中,通过实现拦截器来实现用户登录拦截并验证。

1、SpringBoot 实现登录拦截的原理

SpringBoot 通过实现HandlerInterceptor接口实现拦截器,通过实现WebMvcConfigurer接口实现一个配置类,在配置类中注入拦截器,最后再通过 @Configuration 注解注入配置.

1.1、实现HandlerInterceptor接口

实现HandlerInterceptor接口需要实现 3 个方法:preHandlepostHandleafterCompletion.

3 个方法各自的功能如下:

public class UserLoginInterceptor implements HandlerInterceptor {
/***
* 在请求处理之前进行调用(Controller方法调用之前)
*/

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行了拦截器的preHandle方法");
try {
HttpSession session = request.getSession();
//统一拦截(查询当前session是否存在user)(这里user会在每次登录成功后,写入session)
User user = (User) session.getAttribute(USER_LOGIN_STATE);
if (user != null) {
return true;
}
//重定向登录页面
response.sendRedirect(request.getContextPath() + "/user/login");
} catch (Exception e) {
e.printStackTrace();
}
return false;
//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作
//如果设置为true时,请求将会继续执行后面的操作
}
/***
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行了拦截器的postHandle方法");
}
/***
* 整个请求结束之后被调用,也就是在DispatchServlet渲染了对应的视图之后执行(主要用于进行资源清理工作)
*/

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("执行了拦截器的afterCompletion方法");
}
}

preHandle在 Controller 之前执行,因此拦截器的功能主要就是在这个部分实现:

  1. 检查 session 中是否有user对象存在;
  2. 如果存在,就返回true,那么 Controller 就会继续后面的操作;
  3. 如果不存在,就会重定向到登录界面
    就是通过这个拦截器,使得 Controller 在执行之前,都执行一遍preHandle.

1.2、实现WebMvcConfigurer接口,注册拦截器

实现WebMvcConfigurer接口来实现一个配置类,将上面实现的拦截器的一个对象注册到这个配置类中.

@Configuration
public class LoginConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration = registry.addInterceptor(new UserLoginInterceptor());
//所有路径都被拦截
registration.addPathPatterns("/**");
//添加不拦截路径
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}

将拦截器注册到了拦截器列表中,并且指明了拦截哪些访问路径,不拦截哪些访问路径,不拦截哪些资源文件;最后再以 @Configuration 注解将配置注入。

1.3、保持登录状态

只需一次登录,如果登录过,下一次再访问的时候就无需再次进行登录拦截,可以直接访问网站里面的内容了。

在正确登录之后,就将user保存到session中,再次访问页面的时候,登录拦截器就可以找到这个user对象,就不需要再次拦截到登录界面了.

UserController

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/**
* 发送邮箱验证码
* @return
*/

@PostMapping("/sendCode")
public BaseResponse<String> sendCode(&#64;RequestBody String email) {
// 发送短信验证码并保存验证码
String code &#61; userService.sendCode(email);
return ResultUtils.success(code);
}
/**
* 注册功能
* &#64;param userRegisterRequest
* &#64;return
*/

&#64;PostMapping("/register")
public BaseResponse<Long> register(&#64;RequestBody UserRegisterRequest userRegisterRequest){
if(userRegisterRequest&#61;&#61;null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"请求参数为空");
}
String email &#61; userRegisterRequest.getEmail();
String userpassword &#61; userRegisterRequest.getUserPassword();
String checkpassword &#61; userRegisterRequest.getCheckPassword();
String userName &#61; userRegisterRequest.getName();
String code &#61; userRegisterRequest.getCode();
if(StringUtils.isAnyBlank(email,userpassword,checkpassword,userName,code)){
return null;
}
long result &#61; userService.userRegister(email, userpassword, checkpassword, userName, code);
return ResultUtils.success(result);
}
/**
* 登录功能
* &#64;param userLoginRequest
* &#64;param request
* &#64;return
*/

&#64;PostMapping("/login")
public BaseResponse<User> userdoLogin(&#64;RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request){
if(userLoginRequest&#61;&#61;null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"请求参数为空");
}
String email &#61; userLoginRequest.getEmail();
String password &#61; userLoginRequest.getPassword();
if (StringUtils.isAnyBlank(email,password)){
return null;
}
User result &#61; userService.userdoLogin(email, password,request);
return ResultUtils.success(result);
}
/**
* 登出功能
* &#64;param request
* &#64;return
*/

&#64;PostMapping("/logout")
public BaseResponse<Integer> userlogout(HttpServletRequest request){
if(request&#61;&#61;null){
throw new BusinessException(ErrorCode.NOT_LOGIN,"该用户没有登录");
}
int result &#61; userService.userLogout(request);
return ResultUtils.success(result);
}

UserService

public interface UserService extends IService<User> {
/**
* 发送验证码
* &#64;param email
* &#64;return
*/

String sendCode(String email);
/**
* 用户注册
*
* &#64;param userEmail 用户邮箱
* &#64;param userPassword 用户密码
* &#64;param checkPassword 用户检验密码
*
* &#64;return
*/

long userRegister(String userEmail,String userPassword,String checkPassword,String userName,String code);
/**
* 用户登录
* &#64;param email
* &#64;param password
* &#64;param request
* &#64;return
*/

User userdoLogin(String email, String password, HttpServletRequest request);
/**
* 用户登出
* &#64;param request
* &#64;return
*/

int userLogout(HttpServletRequest request);

UserServiceImpl

&#64;Service
&#64;Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper,User>
implements UserService{
&#64;Resource
private UserMapper userMapper;
&#64;Resource
private StringRedisTemplate stringRedisTemplate;
public static final String SALT &#61; "qgc";
/**
* 发送邮箱验证码
* &#64;param email
* &#64;return
*/

&#64;Override
public String sendCode(String email) {
//1.生成验证码
String code &#61; RandomUtil.randomNumbers(6);
//2.保存验证码到redis中 //set key value ex
stringRedisTemplate.opsForValue().set(code &#43; LOGIN_CODE_KEY, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
//3.发送验证码
log.debug("发送邮箱验证码成功&#xff0c;验证码&#xff1a;{}", code);
return code;
}
/**
* 用户注册
* &#64;param email 邮箱
* &#64;param userPassword 用户密码
* &#64;param checkPassword 用户检验密码
*
* &#64;param userName 用户名字
* &#64;param code 验证码
* &#64;return
*/

&#64;Override
public long userRegister(String email,String userPassword,String checkPassword,String userName,String code) {
//1.校验
if(StringUtils.isAnyBlank(email,userPassword,checkPassword,userName,code)){
throw new BusinessException(PARAMS_ERROR,"请求参数为空");
}
if(userPassword.length() < 8 ||checkPassword.length() < 8){
throw new BusinessException(PARAMS_ERROR,"密码小于8位");
}
if(userName.length()> 10){
throw new BusinessException(PARAMS_ERROR,"名字大于10位");
}
if(code.length() !&#61; 6){
throw new BusinessException(PARAMS_ERROR,"验证码长度应该为6位");
}
//密码和校验密码相同
if(!userPassword.equals(checkPassword)){
throw new BusinessException(PARAMS_ERROR);
}
//账户邮箱不能重复
QueryWrapper<User> queryWrapper &#61; new QueryWrapper<>();
queryWrapper.eq("email",email);
Long count &#61; userMapper.selectCount(queryWrapper);
if (count>0){
throw new BusinessException(PARAMS_ERROR);
}
//昵称不能重复
queryWrapper &#61; new QueryWrapper<>();
queryWrapper.eq("name",userName);
count &#61; userMapper.selectCount(queryWrapper);
if (count>0){
throw new BusinessException(PARAMS_ERROR);
}
//判断验证码是否正确
String cachecode &#61; stringRedisTemplate.opsForValue().get(code &#43; LOGIN_CODE_KEY);
if(cachecode&#61;&#61;null||!cachecode.equals(code)){
//不一致&#xff0c;报错
throw new BusinessException(PARAMS_ERROR);
}
//2.加密
String encryptPassword &#61; DigestUtils.md5DigestAsHex((SALT &#43; userPassword).getBytes(StandardCharsets.UTF_8));
//3.插入数据
User user &#61; new User();
user.setEmail(email);
user.setPassword(encryptPassword);
user.setName(userName);
boolean res &#61; this.save(user);
if(!res){
return -1;
}
return user.getId();
}
/**
* 用户登录
* &#64;param email
* &#64;param password
* &#64;param request
* &#64;return
*/

&#64;Override
public User userdoLogin(String email, String password,HttpServletRequest request) {
//1.校验
if(StringUtils.isAnyBlank(email,password)){
return null;
}
if (RegexUtils.isEmailInvalid(email)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱格式错误");
}
if(password.length() < 8 ){
return null;
}
//2.加密
String encryptPassword &#61; DigestUtils.md5DigestAsHex((SALT &#43; password).getBytes(StandardCharsets.UTF_8));
//判断账户是否存在
QueryWrapper<User> queryWrapper &#61; new QueryWrapper<>();
queryWrapper.eq("email",email);
queryWrapper.eq("password",encryptPassword);
User user &#61; userMapper.selectOne(queryWrapper);
if(user&#61;&#61;null){
log.info("user login failed");
return null;
}
//用户脱敏
User safeUser &#61; getSafeUser(user);
//4.记录用户登录状态
request.getSession().setAttribute(USER_LOGIN_STATE,safeUser);
return safeUser;
}
/**
* 登出功能
* &#64;param request
* &#64;return
*/

&#64;Override
public int userLogout(HttpServletRequest request) {
request.getSession().removeAttribute(USER_LOGIN_STATE);
return 1;
}

springboot拦截器excludePathPatterns方法不生效的坑与解决方法

一、前言

最近在springboot项目里需要配置个拦截器白名单&#xff0c;用excludePathPatterns方法配置些url&#xff0c;让拦截器不拦截这些url&#xff1b;

本来这是个很简单的东西&#xff0c;但是配置完毕后就是没有生效&#xff1b;

在此记录下这个坑的解决方法。

二、问题

1.例如&#xff0c;想让以下url不被拦截器拦截&#xff1a;
http://localhost:8080/api/department/add

2.拦截器配置代码如下&#xff1a;

&#64;Configuration
public class LoginConfig implements WebMvcConfigurer {
&#64;Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration &#61; registry.addInterceptor(new UserLoginInterceptor());
//所有路径都被拦截
registration.addPathPatterns("/**");
//添加不拦截路径
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/api/department/add"
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}

3.看起来没有问题&#xff0c;但是当访问上方url的时候&#xff0c;还是会被拦截器拦截&#xff0c;就很坑。

三、解决方法

1.通过排查发现&#xff0c;原来&#xff0c;在application.yml中&#xff0c;是这样配置的&#xff1a;

server:
port: 8080
servlet:
context-path: /api

2.所以&#xff0c;还是拦截器的url配置错了&#xff0c;想不拦截的话&#xff0c;需要这样配置&#xff1a;

&#64;Configuration
public class LoginConfig implements WebMvcConfigurer {
&#64;Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration &#61; registry.addInterceptor(new UserLoginInterceptor());
//所有路径都被拦截
registration.addPathPatterns("/**");
//添加不拦截路径
registration.excludePathPatterns(
"/user/login",
"/user/register",
"/department/add"
"/**/*.html",
"/**/*.js",
"/**/*.css"
);
}
}

3.这样&#xff0c;访问这个url&#xff0c;才能不被拦截器拦截&#xff1a;
http://localhost:8080/survey-project/download/special

四、总结

1.配置拦截器时&#xff0c;如果excludePathPatterns没有生效&#xff0c;可能是url配置有问题。
2.可以检查下application.yml的context-path&#xff0c;或者其它类似的地方&#xff0c;配置拦截器的url不应该包含这些路径&#xff0c;只要从Controller的路径开始配置即可。

五、扩展

使用response对象的sendRedirect()方法将用户的请求重定向到指定路径&#xff0c;这个路径由request对象的getContextPath()方法获取&#xff0c;再加上字符串 “/” 组成。getContextPath()方法返回当前web应用程序的上下文路径&#xff0c;此处加的字符串路径也是从Controller的路径开始配置即可

“/user/login”

//重定向登录页面
response.sendRedirect(request.getContextPath() &#43; "/user/login");

会被重定向到

http://127.0.0.1:8080/user/login

image-20230215124957402

“/login”

//重定向登录页面
response.sendRedirect(request.getContextPath() &#43; "/login");

http://127.0.0.1:8080/login

image-20230215125144089

"/user/login"也是从Controller的路径开始配置

参考博文&#xff1a;SpringBoot实现登录拦截器&#xff08;实战版&#xff09;






推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
author-avatar
保定联通员工
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有