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

news2024/10/2 6:28:14

文章目录

  • 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(@RequestBody String email) {
        // 发送短信验证码并保存验证码
        String code = userService.sendCode(email);
        return ResultUtils.success(code);
    }

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

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

    /**
     * 登出功能
     * @param request
     * @return
     */
    @PostMapping("/logout")
    public BaseResponse<Integer> userlogout(HttpServletRequest request){
        if(request==null){
            throw new BusinessException(ErrorCode.NOT_LOGIN,"该用户没有登录");
        }
        int result = userService.userLogout(request);
        return ResultUtils.success(result);
    }

UserService

public interface UserService extends IService<User> {
    /**
     * 发送验证码
     * @param email
     * @return
     */
    String sendCode(String email);

    /**
     * 用户注册
     *
     * @param userEmail 用户邮箱
     * @param userPassword 用户密码
     * @param checkPassword 用户检验密码
     *
     * @return
     */
    long userRegister(String userEmail,String userPassword,String checkPassword,String userName,String code);

    /**
     * 用户登录
     * @param email
     * @param password
     * @param request
     * @return
     */
    User userdoLogin(String email, String password, HttpServletRequest request);
     /**
     * 用户登出
     * @param request
     * @return
     */
    int userLogout(HttpServletRequest request);

UserServiceImpl

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper,User>
        implements UserService{
    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String SALT = "qgc";

    /**
     * 发送邮箱验证码
     * @param email
     * @return
     */
    @Override
    public String sendCode(String email) {
        //1.生成验证码
        String code = RandomUtil.randomNumbers(6);
        //2.保存验证码到redis中  //set key value ex
        stringRedisTemplate.opsForValue().set(code + LOGIN_CODE_KEY, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //3.发送验证码
        log.debug("发送邮箱验证码成功,验证码:{}", code);
        return code;
    }

    /**
     * 用户注册
     * @param email 邮箱
     * @param userPassword 用户密码
     * @param checkPassword 用户检验密码
     *
     * @param userName 用户名字
     * @param code 验证码
     * @return
     */
    @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() != 6){
            throw new BusinessException(PARAMS_ERROR,"验证码长度应该为6位");
        }
        //密码和校验密码相同
        if(!userPassword.equals(checkPassword)){
            throw new BusinessException(PARAMS_ERROR);
        }
        //账户邮箱不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email",email);
        Long count = userMapper.selectCount(queryWrapper);
        if (count>0){
            throw new BusinessException(PARAMS_ERROR);
        }
        //昵称不能重复
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name",userName);
        count = userMapper.selectCount(queryWrapper);
        if (count>0){
            throw new BusinessException(PARAMS_ERROR);
        }
        //判断验证码是否正确
        String cachecode = stringRedisTemplate.opsForValue().get(code + LOGIN_CODE_KEY);
        if(cachecode==null||!cachecode.equals(code)){
            //不一致,报错
            throw new BusinessException(PARAMS_ERROR);
        }
        //2.加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes(StandardCharsets.UTF_8));
        //3.插入数据
        User user = new User();
        user.setEmail(email);
        user.setPassword(encryptPassword);
        user.setName(userName);
        boolean res = this.save(user);
        if(!res){
            return -1;
        }
        return user.getId();
    }

    /**
     * 用户登录
     * @param email
     * @param password
     * @param request
     * @return
     */
    @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 = DigestUtils.md5DigestAsHex((SALT + password).getBytes(StandardCharsets.UTF_8));
        //判断账户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email",email);
        queryWrapper.eq("password",encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if(user==null){
            log.info("user login failed");
            return null;
        }
        //用户脱敏
        User safeUser = getSafeUser(user);
        //4.记录用户登录状态
        request.getSession().setAttribute(USER_LOGIN_STATE,safeUser);
        return safeUser;
    }
    /**
     * 登出功能
     * @param request
     * @return
     */
    @Override
    public int userLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return 1;
    }

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

一、前言

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

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

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

二、问题

1.例如,想让以下url不被拦截器拦截:
http://localhost:8080/api/department/add

2.拦截器配置代码如下:

@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",
                "/api/department/add"
                "/**/*.html",
                "/**/*.js",
                "/**/*.css"
        );
    }
}

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

三、解决方法

1.通过排查发现,原来,在application.yml中,是这样配置的:

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

2.所以,还是拦截器的url配置错了,想不拦截的话,需要这样配置:

@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",
                "/department/add"
                "/**/*.html",
                "/**/*.js",
                "/**/*.css"
        );
    }
}

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

四、总结

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

五、扩展

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

“/user/login”

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

会被重定向到

http://127.0.0.1:8080/user/login

image-20230215124957402

“/login”

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

http://127.0.0.1:8080/login

image-20230215125144089

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

参考博文:SpringBoot实现登录拦截器(实战版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言编译过程

C语言编译过程1、C语言编译过程2、单c文件编译实践3、多c文件编译实践4、define4.1、不带参宏4.2、带参宏4.3、带参宏和带参函数的区别5、选择性编译ifdef、ifndef、if5.1、#ifdef5.2、#ifndef5.3、#if6、静态库和动态链接库6.1、静态库实践6.1.1、将mylib.c制作成静态库6.1.2、…

Baklib知识库管理平台,协助组织提升知识管理水平

随着信息时代和知识经济时代的到来&#xff0c;企业内部信息资料繁多冗杂&#xff0c;知识管理逐渐成为各大企业的重要工作之一&#xff0c;企业管理者无不感受到巨大的压力&#xff0c;怎么样将知识进行有效的管理&#xff0c;成为一个难点&#xff0c;并且随着信息不断的更迭…

Java企业级信息系统开发学习笔记(1)初探Spring与骑士傻龙实例

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【http://t.csdn.cn/iK3aM】 文章目录1. 创建Maven项目2. 添加Spring依赖3. 创建杀龙任务4. 创建勇士类5. 采用传统的方式6. 采用Spring容器让勇敢骑士完成杀龙任务6.1 创建日志属性文件6.2 创建Spring配置文件6.3 在…

CobaltStrike上线微信通知

CobaltStrike上线微信通知 利用pushplus公众号&#xff08;每天免费发送200条消息&#xff09; http://www.pushplus.plus/push1.html 扫码登录后需要复制token 可以测试一下发送一下消息&#xff0c;手机会受到如下消息。可以在微信提示里将消息免打扰关闭&#xff08;默认…

分布式光伏储能系统的优化配置方法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

期末复习提纲

复习提纲 题型&#xff1a;编程题3题&#xff0c;综合题4题。 一、编程题&#xff1a; 1、链表的类型定义&#xff1b;邻接矩阵表示图的类型定义&#xff1b;链接表表示图的类型定义&#xff1b;vector数组表示图的定义和使用方法。 2、链表中结点的插入和删除操作&#xff…

linux高级命令之编辑器 vim

编辑器 vim学习目标能够说出vim的三种工作模式能够说出vim对应复制和粘贴命令1. vim 的介绍vim 是一款功能强大的文本编辑器&#xff0c;也是早年 Vi 编辑器的加强版&#xff0c;它的最大特色就是使用命令进行编辑&#xff0c;完全脱离了鼠标的操作。2. vim 的工作模式命令模式…

2023想转行软件测试的看过来,你想要了解的薪资、前景、岗位方向、学习路线都讲明白了

在过去的一年中&#xff0c;软件测试行业发展迅速&#xff0c;随着数字化技术应用的广泛普及&#xff0c;业界对于软件测试的要求也在持续迭代与增加。 同样的&#xff0c;有市场就有需求&#xff0c;软件测试逐渐成为企业中不可或缺的岗位&#xff0c;作为一个高薪又需求广的…

怎么解密MD5,常见的MD5解密方法,一看就会

MD5是一种被广泛使用的密码散列函数&#xff0c;曾在计算机安全领域使用很广泛&#xff0c;但是也因为它容易发生碰撞&#xff0c;而被人们认为不安全。那么&#xff0c;MD5应用场景有哪些&#xff0c;我们怎么解密MD5&#xff0c;本文将带大家了解MD5的相关知识&#xff0c;以…

Laravel创建定时任务

创建一个任务&#xff0c;创建成功后会在App/Console/Commands中生成一个以Test命名的文件&#xff0c;我们可以在这里面写我们的任务指令。 php artisan make:command Test 运行这个定时任务 run 是运行一次&#xff0c;我们可以用来测试是否成功&#xff0c;work是一直运行&a…

Jenkins的使用教程

介绍&#xff1a; Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 目的: 最重要目的就是把原来分散在各个机器上繁杂的工作全部…

hadoop高可用搭建

修改计算机名称 //修改计算机名称 [rootlocalhost ~]# hostnamectl set-hostname ant150//快速生效 [rootlocalhost ~]# bash 主机名称映射 [rootant150 ~]# vim /etc/hosts 192.168.153.150 ant150 192.168.153.151 ant151 192.168.153.152 ant152 192.168.153.153 ant153 …

数据分析-深度学习 Tensorflow Day6

我们需要解决的问题&#xff1a;1&#xff1a; 什么是bp 神经网络&#xff1f;2&#xff1a;理解bp神经网络需要哪些数学知识&#xff1f;3&#xff1a;梯度下降的原理4: 激活函数5&#xff1a;bp的推导。1.什么是bp网络&#xff1f;引用百度知道回复&#xff1a;“我们最常用的…

【mmrotate】旋转目标检测之训练DOTA数据集

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 mmrotate训练DOTA数据集记录 1. 正文 1.1 数据准备 数据介绍部分&#xff0c;参考DOTA数据介绍&#xff0c;官方提供了裁剪工具development kit。这里…

LINUX内核链表

LINUX内核链表 一、传统链表的缺陷 传统的双向循环链表概念简单&#xff0c;操作方便&#xff0c;但存在有致命的缺陷&#xff0c;用一句话来概括就是&#xff1a; 每一条链表都是特殊的&#xff0c;不具有通用性。换句话说&#xff0c;对于每一种不同的数据&#xff0c;所构…

java对象克隆和面向对象的设计原则

java进阶注解内置注解元注解自定义注解对象克隆浅克隆深克隆java设计模式建模语言类之间的关系依赖关系关联关系单向关联双向关联自关联聚合关系组合关系继承关系实现关系面向对象设计原则单一职责开闭原则里氏替换原则依赖倒置接口隔离迪米特原则组合/聚合复用原则注解 java注…

关于ucharts在小程序中的使用

项目添加组件 画图之前&#xff0c;首先需要引入ucharts组件&#xff0c;引入链接https://ext.dcloud.net.cn/plugin?id271。 点击下图中红色方框内容&#xff1a; 导入完成后&#xff0c;与uni其他组件一样&#xff0c;无需引入&#xff0c;直接使用即可。 使用组件 uchar…

jhipster自动生成java代码的方法

一、前言 java springboot后台项目用到了jpa查询数据库&#xff0c;还用到了jhipster&#xff0c;这个东西可以自动生成基础的Controller、Service、Dao、JavaBean等相关代码&#xff0c;减少重复开发。 在此总结下使用方法。 二、jhipster自动生成java代码的方法 1.需要先…

KDHX-8700无线高压核相相序表

一、产品简介 KDHX-8700无线高压核相相序表&#xff08;以下简称“仪器”&#xff09;用于测定三相线相序、检测环网或双电源电力网闭环点断路器两侧电源是否同相。在闭环两电源之前一定要进行核相操作&#xff0c;否则可能发生短路。仪器适用于380V&#xff5e;35kV交流输电线…

JavaScript void

文章目录JavaScript voidjavascript:void(0) 含义href"#"与href"javascript:void(0)"的区别JavaScript void javascript:void(0) 含义 我们经常会使用到 javascript:void(0) 这样的代码&#xff0c;那么在 JavaScript 中 javascript:void(0) 代表的是什么…