您现在的位置是:首页 > 正文

基于注解的用户权限拦截Spring HandlerInterceptor

2024-04-01 00:47:51阅读 2

Spring Boot (v2.0.5.RELEASE)

  • 程序中有些资源(接口)是需要用户登录才能够使用的,或者是具有某种角色的用户(比如普通登录用户,或者系统管理员等)才能使用,本篇文章先为大家讲解如何控制使用某接口要求用户必须登录。

  • 实现的思路是

  1. 首先定义注解 @LoginUser,该注解用于标注哪些接口需要进行拦截

  2. 定义拦截器,拦截标注了 @LoginUser注解的接口

  3. 拦截之后判断该用户目前是不是处于登陆状态,如果是登陆状态则放行该请求,如果未登录则提示登陆

  4. 给方法或者类打上 @LoginUser注解进行测试

1. 定义标注注解 @LoginUser

package com.futao.springmvcdemo.annotation;
import com.futao.springmvcdemo.model.enums.Role;
import java.lang.annotation.*;
/**
 * @author futao
 * Created on 2018/9/19-14:39.
 * 登陆用户,用户角色
 */
@Target(value = {
        ElementType.METHOD,
        ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginUser {
    /**
     * 要求的用户角色
     *
     * @return
     */
    Role role() default Role.Normal;
}

2. 定义拦截器 LoginUserInterceptor

package com.futao.springmvcdemo.annotation.impl;
import com.alibaba.fastjson.JSON;
import com.futao.springmvcdemo.annotation.LoginUser;
import com.futao.springmvcdemo.model.entity.constvar.ErrorMessage;
import com.futao.springmvcdemo.model.system.RestResult;
import com.futao.springmvcdemo.model.system.SystemConfig;
import com.futao.springmvcdemo.utils.ThreadLocalUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
 * @author futao
 * Created on 2018/9/19-14:44.
 * 对请求标记了LoginUser的方法进行拦截
 */
@Component
public class LoginUserInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(LoginUserInterceptor.class);
    @Resource
    private ThreadLocalUtils<String> threadLocalUtils;
    /**
     * 在请求到达Controller之前进行拦截并处理
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            //注解在方法上
            LoginUser loginUserAnnotation = ((HandlerMethod) handler).getMethodAnnotation(LoginUser.class);
            //注解在类上
            LoginUser classLoginUserAnnotation = ((HandlerMethod) handler).getMethod().getDeclaringClass().getAnnotation(LoginUser.class);
            if (ObjectUtils.anyNotNull(loginUserAnnotation, classLoginUserAnnotation)) {
                HttpSession session = request.getSession(false);
                //session不为空
                if (ObjectUtils.allNotNull(session)) {
                    String loginUser = (String) session.getAttribute(SystemConfig.LOGIN_USER_SESSION_KEY);
                    if (ObjectUtils.allNotNull(loginUser)) {
                        System.out.println("当前登陆用户为:" + loginUser);
                        //将当前用户的信息存入threadLocal中
                        threadLocalUtils.set(loginUser);
                    } else {
                        System.out.println("用户不存在");
                        return false;
                    }
                } else {//session为空,用户未登录
                    RestResult restResult = new RestResult(false, "-1", ErrorMessage.NOT_LOGIN, ErrorMessage.NOT_LOGIN.substring(6));
                    response.getWriter().append(JSON.toJSONString(restResult));
                    return false;
                }
            }
        }
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //释放threadLocal资源
        threadLocalUtils.remove();
    }
}

3. 注册拦截器

package com.futao.springmvcdemo.annotation;
import com.futao.springmvcdemo.annotation.impl.LoginUserInterceptor;
import com.futao.springmvcdemo.annotation.impl.RequestLogInterceptor;
import com.futao.springmvcdemo.annotation.impl.SignInterceptor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
 * @author futao
 * Created on 2018/9/18-15:15.
 */
@SpringBootConfiguration
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Resource
    private SignInterceptor signInterceptor;
    @Resource
    private LoginUserInterceptor loginUserInterceptor;
    @Resource
    private RequestLogInterceptor requestLogInterceptor;
    /**
     * addInterceptor()的顺序需要严格按照程序的执行的顺序
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestLogInterceptor).addPathPatterns("/**");
        registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");
        //  "/**"和"/*"是有区别的
        registry.addInterceptor(signInterceptor).addPathPatterns("/**");
    }
}

4. 测试(可分别将注解打在类上和方法上进行测试)

package com.futao.springmvcdemo.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.futao.springmvcdemo.annotation.LoginUser;
import com.futao.springmvcdemo.model.entity.User;
import com.futao.springmvcdemo.model.system.SystemConfig;
import com.futao.springmvcdemo.service.UserService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.UUID;
/**
 * @author futao
 * Created on 2018/9/19-15:05.
 */
@RequestMapping(path = "User", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@RestController
public class UserController {
    @Resource
    private UserService userService;
    /**
     * 获取当前的登陆的用户信息,其实是从threadLocal中获取
     *
     * @return
     */
    @LoginUser
    @GetMapping(path = "my")
    public JSONObject my() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("当前的登陆的用户是:", userService.currentUser());
        return jsonObject;
    }
    /**
     * 模拟登陆接口
     *
     * @param mobile
     * @param request
     * @return
     */
    @PostMapping(path = "login")
    public JSONObject login(
            @RequestParam("mobile") String mobile,
            HttpServletRequest request
    ) {
        HttpSession session = request.getSession();
        session.setAttribute(SystemConfig.LOGIN_USER_SESSION_KEY, String.valueOf(UUID.randomUUID()));
        session.setMaxInactiveInterval(SystemConfig.SESSION_INVALIDATE_SECOND);
        return new JSONObject();
    }
}

4.1 测试未登录情况下调用标记了 @LoginUser的获取当前登陆用户信息接口 

4.2 登录 

4.3 登录之后调用调用标记了 @LoginUser的获取当前登陆用户信息接口 

稍微解释一下上面登陆和获取用户信息的逻辑: 用户请求登陆之后,会为该用户在系统中生成一个 HttpSession,同时在系统中有一个 Map来存放所有的 session信息,该 Map的 key为一个随机字符串, value为 session对象在系统中的堆地址,在登陆请求完成之后,系统会将该 sesion的 key值以 cookie(JSESSIONID)的形式写回浏览器。 用户下次登陆的时候,请求中会自动带上该 cookie,所以我们在标记了需要登陆的 @LoginUser注解的请求到达处理逻辑之前进行拦截,就是从 cookie中(JSESSIONID)取出 session的 key值,如果没有该 cookie,则代表用户没有登陆,如果有该 cookie,再在存放 cookie的 map中取,如果没有取到,则代表用户的 session已经过期了,需要重新登陆,或者 cookie是伪造的。 拿到了登陆用户的 session之后,我们去 Map中获取对应的值,一般是用户的 id,在通过这个用户 id,可以去数据库查该用户的信息,查到用户的信息之后将用户信息放入 threadLocal中,然后就可以在任何地方 get()到当前登陆的用户信息了,非常方便。

使用上面的基于注解的拦截器可以实现很多功能,比如动态的第三方接口验签,和系统日志记录(不需要注解)等 

网站文章

  • Laravel 打印SQL语句

    类class之前记得引用use Illuminate\Support\Facades\DB; 在方法里面这样干 DB::connection()-&gt;enableQueryLog();#开启执行日...

    2024-04-01 00:47:27
  • 大一大学计算机心得,大学生计算机实训心得体会

    大学生计算机实训心得体会是关于心得体会范文的精选内容,内容包含了与大学生计算机实训心得体会相关的经典语句以及心得体会范文大全、心得体会范文范文、心得体会范文模板等相关资料,为朋友们查找心得体会范文的资...

    2024-04-01 00:47:18
  • BugkuCTF-WEB部分题解(一)

    BugkuCTF-WEB部分题解(一)

    域名解析 访问 flag.baidu.com 提示:找不到网站 无法进行DNS解析 直接访问123.206.87.240 返回400 Bad Request 查询400错误 详情 两种方法解析域名 1.修改hosts文件: 直接在hosts文件中添加flag.bugku.com 120.24.86.145 2.修改头信息: 用burpsuit抓包修改host头信息为flag.bugku...

    2024-04-01 00:47:10
  • HTML5新增元素和移除的元素?

    新增元素:图像Canvas多媒体video、audio本地存储localStorage、sessionStorage语义化更好的内容元素aticle、header、footer、nav、section表单控件date、time、canlendar、url、search新的技术webworker、websocket、Geolocation移除的元...

    2024-04-01 00:46:44
  • mysql数据库基础

    mysql数据库基础

    数据库的基本介绍相关概念DBMS:数据库管理系统,就是数据库软件数据库:保持有组织的数据的容器(通常是一个文件和一组文件),不管数据是什么或如何组织的,是通过DBMS创建和操纵的容器。SQL:结构化查询语句,是专门用来与数据库沟通的语言,需要一个支持SQL语句执行的应用程序表:某种特定类型数据的结构化清单,存储在表中的数据是同一种类型的数据或清单主流数据库介绍Oracle: 甲...

    2024-04-01 00:46:38
  • Android List与json的相互转换

    2、写一个AddressBean实体类。1、fastjson框架导入。2、List转json。3、json转List。

    2024-04-01 00:46:30
  • (二)图像处理技术概述

    (二)图像处理技术概述

    目录 2.1 图像处理技术概述 2.2 图像成像过程 2.2.1 视觉过程 2.2.2 成像变换 2.2.3 成像亮度 2.2.4 视觉系统对光的感知特点 2.2.5 采样和量化 2.3 像素间联系 ...

    2024-04-01 00:46:23
  • Maven开发环境搭建

    Maven开发环境搭建

    在进行Maven开发环境搭建之前我们必须要有两个前提1.我们的电脑已经安装并配置了jdk;2.因为Maven工作时经常需要通过网络下载各种所需文件,因此我们需要保证网络的通常.

    2024-04-01 00:45:58
  • JSP指令及其重要内置对象、作用域

    4.JSP的指令 *指令的语法: &lt;%@ 指令名称 属性名称=”属性值” 属性名称=”属性值” …%&gt; JSP中有三个指令: page指令, include指令, taglib指令. 【面试可能会问到】★★★★★5.JSP指令之page指令 ———-设置JSP的 ★★★★ *contentType :设置浏览器打开...

    2024-04-01 00:45:52
  • JAVA对象及属性的内存堆栈管理(通过小程序简单说明)

    JAVA在执行过程中会划分4个内存区域(heap、stack、data segment、code segment)代码区(codesegment):java开始执行会把代码加载到code segment区域然后找到main方法开始执行静态区(datasegment): 是存放静态变量字符串常量堆区(heapsegment): 一般由程序员分配释放,存放由new创建的对象和数组,jvm不定时查看这个

    2024-04-01 00:45:43