SpringBoot自定义登录、权限验证

news2025/1/24 11:40:19

1、首先最基础的User实体类,使用了lombok,所以省略了getter、setter方法

@Data
public class UserInfo implements Serializable {
    private Integer id;
    //用户名
    private String username;
    //密码不需要被序列化存入redis
    private transient String password;
    //登录过期时间
    private Long expireTime;
    //登录成功后的token
    private String token;
    //权限集
    private Set<String> permissions;

    public UserInfo() {
    }

    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public UserInfo(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

}

2、redis基础操作,登录成功后,信息保存到redis中

@Component
public class RedisCache {
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 缓存带有过期时间的对象
     * @param key       缓存的键值
     * @param value     缓存的值
     * @param time      过期时间
     * @param timeUnit  时间单位
     */
    public <T> void setCacheObjectExpire(final String key, final T value, final Long time, final TimeUnit timeUnit){
        redisTemplate.opsForValue().set(key,value,time,timeUnit);
    }
}

3、登录成功后token相关处理

@Component
public class JwtTokenService {
    @Autowired
    RedisCache redisCache;

    //前端请求token对应的header中的key
    private final static String TOKEN_KEY =  "C-Token";
    //jwt加密密钥
    private final static String SIGNING_KEY =  "xice202304181537";
    //密码过期时间
    private final static Long EXPIRE_TIME = 30L;
    //密码过期时间单位
    private final static TimeUnit EXPIRE_TIME_UNIT = TimeUnit.MINUTES;
    //token刷新时间间隔
    private final static Long REFRESH_EXPIRE_TIME = 20 * 60 * 1000L;


    /**
     * 登录成功,生成token
     * @param userInfo
     * @return
     */
    public String createToken(UserInfo userInfo){
        //生成uuid,用着redis的key、token
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        //生成token
        Map claims = new HashMap(1);
        claims.put(TOKEN_KEY,uuid);
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, SIGNING_KEY).compact();
        //保存token
        userInfo.setToken(token);
        //这是过期时间
        userInfo.setExpireTime(System.currentTimeMillis() + REFRESH_EXPIRE_TIME * 2);
        //获取权限资源,此处应该查询数据库,方便测试,直接写死
        Set<String> permissions = new HashSet<>();
        permissions.add("/user/getUserInfo");
        userInfo.setPermissions(permissions);
        //存入redis
        redisCache.setCacheObjectExpire(uuid,userInfo,EXPIRE_TIME,EXPIRE_TIME_UNIT);
        return token;
    }

    /**
     * 根据token获取用户信息
     * @param request
     * @return
     */
    public UserInfo getUserByToken(HttpServletRequest request){
        String token = request.getHeader(TOKEN_KEY);
        Claims claims = Jwts.parser()
                .setSigningKey(SIGNING_KEY)
                .parseClaimsJws(token)
                .getBody();
        //解密token
        String uuid = (String) claims.get(TOKEN_KEY);
        //通过uuid获取redis中存的用户信息
        return redisCache.getCacheObject(uuid);
    }

    /**
     * 刷新token
     * @param userInfo
     */
    public void refreshToken(UserInfo userInfo){
        long now = System.currentTimeMillis();
        //当过期时间与当时时间小于指定的刷新时间间隔是,延长redis中信息的时间
        if(userInfo.getExpireTime() - now  <= REFRESH_EXPIRE_TIME){
            System.out.println("刷新token...");
            redisCache.setCacheObjectExpire(userInfo.getToken(),userInfo,EXPIRE_TIME,EXPIRE_TIME_UNIT);
        }
    }
}

4、声明注解,只有加了自定义注解的方法,才进行权限验证

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CAuth {
}

5、使用HandlerInterceptor拦截器进行权限验证,详细方法

@Configuration
public class AuthConfig implements HandlerInterceptor {
    @Autowired
    JwtTokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //静态资源不拦截
        if(handler instanceof ResourceHttpRequestHandler){
            return true;
        }
        //获取注解
        Annotation[] annotations = ((HandlerMethod) handler).getMethod().getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            //添加了CAuth注解的方法需要登录后才能访问
            if(annotation.annotationType().isAssignableFrom(CAuth.class)){
                //查看是否登录
                UserInfo loginUser = tokenService.getUserByToken(request);
                if(ObjectUtils.isEmpty(loginUser)){
                    response.setContentType("text/html;charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    writer.print("请先登录!");
                    writer.flush();
                    writer.close();
                    return false;
                }else{
                    //判断是否有权限访问当前资源
                    String requestURI = request.getRequestURI();
                    return loginUser.getPermissions().stream().allMatch(perm -> {
                        if (requestURI.equalsIgnoreCase(perm)) {
                            return true;
                        }
                        response.setContentType("text/html;charset=utf-8");
                        PrintWriter writer = null;
                        try {
                            writer = response.getWriter();
                            writer.print("您没有权限进行此操作!");
                            writer.flush();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            writer.close();
                        }
                        return false;
                    });
                }
            }
        }
        return true;
    }

}

6、添加拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    AuthConfig authConfig;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authConfig);
    }
}

7、登录和验证方法

@RestController
@RequestMapping("user")
public class UserInfoController {
    @Autowired
    JwtTokenService jwtTokenService;

    @RequestMapping("login")
    public String login(@RequestBody UserInfo userInfo){
        if("xice".equals(userInfo.getUsername())&&"123456".equals(userInfo.getPassword())){
            String token = jwtTokenService.createToken(userInfo);
            return token;
        }else{
            return "用户名或密码不正确!";
        }

    }

    @CAuth
    @RequestMapping("getUserInfo")
    public UserInfo getUserInfo(HttpServletRequest request){
        UserInfo userInfo = jwtTokenService.getUserByToken(request);
        jwtTokenService.refreshToken(userInfo);
        return userInfo;
    }

    @CAuth
    @RequestMapping("test")
    public void test(HttpServletRequest request){
        System.out.println("test。。。");
    }
}

登录请求 /user/login 没有添加 @CAuth注解,所以不会进行拦截,登录成功后返回一个token值,后续请求需要在header中增加C-Token:token参数

/user/getUserInfo:登陆后有此资源权限,可以获取到当前用户信息

/user/test:无此资源权限,会返回 “您没有权限进行此操作!”

 

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

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

相关文章

vue3类型uniapp调用signalr

目录 背景 安装 renderjs 1选择一个tab页面承载renderjs代码 2编写业务逻辑代码 3编写renderjs代码 背景 后端使用.net6开发&#xff0c;长链接选择了微软的signalr而非原生的websocket 前端uniapp下vue3类型开发的app&#xff0c;需要通过长链接获取后端推送的消息 安…

通过对话了解cookie session与token的用途和区别

1 先来了解cookie与localstorage 1.1 http的无状态 用户: 我想看csdn我有多少粉丝了(http请求) 服务器:你是&#xff1f;请告诉我你的名字和密码&#xff0c;我确认你是谁 用户&#xff1a;发起登录请求 admin 123456 服务器&#xff1a;ok&#xff0c;登录成功 用户&…

分享几个国内免费的ChatGPT镜像网址(亲测有效)

最近由于ChatGPT的爆火也让很多小伙伴想去感受一下ChatGPT的魅力&#xff0c;那么今天就分享几个ChatGPT国内的镜像网址&#xff0c;大家可以直接使用&#xff01;记得点赞收藏一下呦&#xff01; 1、AQ Bot&#xff0c;网址&#xff1a;点我 https://su.askaiw.com/aq 缺点&…

搭建CDH流程记录

搭建CDH流程记录 如何搭建本地yum源 1.配置yum源这里使用 阿里源 http://mirrors.aliyun.com/repo/Centos-7.repo wget http://mirrors.aliyun.com/repo/Centos-7.repo2.安装http软件 yum install httpd -y3.配置httpd.conf vi /etc/httpd/conf/httpd.conf在 AddType appli…

酒店行业开启“狂飙”,尚美数智稳步领跑

文|智能相对论 作者|范柔丝 在消费行业迅速复苏的浪潮下&#xff0c;无论从销量还是数量来看&#xff0c;酒旅行业蛰伏三年后&#xff0c;终于开启了业绩狂飙。 从数量来看&#xff0c;企查查数据显示&#xff0c;截至目前&#xff0c;我国现存酒店相关企业233.5万家&#x…

Grafana链接跳转与值传递,把表格变量值从一个dashboard传递给另一个dashboard

文章目录 1. 创建两个空白 Dashboard 用于实验2. dash_1&#xff1a;创建跳转用的表格2. dash_2&#xff1a;配置接收数据的变量 Variables3. 测试跳转4. 通过跳转的变量传递方法总结 这里&#xff0c;我们一步步的来&#xff0c;通过配置一个页面跳转的效果&#xff0c;把一个…

二结(4.18)项目进度

今天学长上了多线程的课程&#xff0c;内容挺广泛的&#xff0c;部分也需要实际运用到项目中来&#xff0c;但我的登录、注册实现还没区分开服务端和客户端&#xff08;仅在同一项目里实现&#xff09; --------------------------------------------------------------------…

【分布式系统】分布式系统架构的冰与火

什么是分布式系统 分布式系统&#xff08;distributed system&#xff09;是建立在网络之上的软件系统。 以上是摘自百度百科的解释&#xff0c;不可否则&#xff0c;分布式系统的基础是网络、计算、存储。比如常见的一个Web单体系统&#xff0c;其实也是一个分布式系统&#x…

Android监听消息(二)——电话及短信监听

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为2747字&#xff0c;预计阅读6分钟 前言 前面一篇《Android监听消息&#xff08;一&#xff09;——应用消息捕获》我们使用NotificationListenerService实现了应用的消息监听&#xff0c;但是电话和短信是…

【花雕学AI】爆款ChatGPT的核心算法和技术逻辑到底是什么?

一、ChatGPT是一种基于GPT模型的聊天机器人 由OpenAI研究中心开发&#xff0c;于2022年11月30日发布。它可以根据用户的输入&#xff0c;生成自然、流畅、有趣的对话回复。它的技术逻辑主要是利用大规模的预训练语言模型&#xff08;LLM&#xff09;&#xff0c;通过Transforme…

总结823

学习目标&#xff1a; 4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 学习内容&#xff1a; 暴力英语&#xff1a;早上1.5小时背单词&#xff0c;背了两篇文章&#xff0c;之后抄写5篇文章。晚上做了一道长难句。 …

CAN-FD协议

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录 CAN-FD协议**CAN-FD协议需要什么&#xff1f;**CAN-FD 协议的属性CAN-FD 协议中的安全性 OSI 层中的 CAN-FD**CAN-FD物理层设计**CAN-FD 数据链路层数据链路层…

【2023】cookie是什么?有什么用?一篇文章彻底搞懂cookie

一个不大不小的问题 假设服务器有一个接口&#xff0c;通过请求这个接口&#xff0c;可以添加一个管理员 但是&#xff0c;不是任何人都有权力做这种操作的 那么服务器如何知道请求接口的人是有权力的呢&#xff1f; 答案是&#xff1a;只有登录过的管理员才能做这种操作 …

一天掌握C51单片机基础1-计算机数值与MCS51单片机

目录 简介计算机的数值表示源码反码补码 MCS51 单片机型号与构成存储结构外部引脚与总线接口并行 IO 口工作原理工作周期 简介 本笔记参考B站高宏亮老师的教学视频&#xff1a;点击观看 计算机的数值表示 源码 正数&#xff1a;首位 0&#xff0c;其余七位表示实际数值 负数&…

Java内存模型JMM

大厂面试题&#xff1f; 你知道什么是java内存模型JMM吗&#xff1f; JMM和Volatile它们两个之间的关系&#xff1f; JMM有哪些特性和他的三大特性是什么&#xff1f; 为什么要有JMM&#xff0c;他为什么出现&#xff1f;作用和功能是什么&#xff1f; happens-before先行发…

如何用jmeter+ant+jenkins搭建一个接口自动化测试框架?

目录 前言 一、什么是Jmeter&#xff1f; 二、什么是Ant&#xff1f; 三、什么是Jenkins&#xff1f; 四、如何构建一个JmeterAntJenkins的接口自动化测试框架&#xff1f; 五、JmeterAntJenkins接口自动化测试框架的优势和特点 六、总结 前言 Jmeter是一款功能强大的开…

陪诊小程序开发|陪诊软件开发功能特色

为了提升就医的服务质量&#xff0c;人们对于医疗服务的需求也在不断提高。这几年随着生活水平和医疗水平的提升&#xff0c;陪诊服务越来越受到人们的重视和青睐&#xff0c;越来越多的人开始意识到&#xff0c;陪伴和关爱在疾病治疗过程中的重要性&#xff0c;为了更好的规划…

Python数据结构与算法-贪心算法(一)

一、贪心算法 1、定义 贪心算法(贪婪算法)是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;他所作出的是在某种意义上的局部最优解。 贪心算法并不保证会得到最优解&#xff0c;但是在某些…

debian 10 安装prometheus 2.37.6 配置rc.local自启动

debian 10 安装prometheus 2.37.6 配置rc.local自启动 1、下载安装包2、安装3、访问普罗米修斯4、加入开机自启动4.1、配置rc-local.service4.2、添加自定义启动命令4.3、查看rc-local.service 1、下载安装包 https://prometheus.io/download/ wget -c https://github.com/pro…

人工智能前沿——「小海带」超全视觉注意力机制资源分享(附下载链接)

&#x1f4da;&#x1f4da; 人工智能 | 计算机视觉 —— 致力于目标检测领域科研Tricks改进与推荐 | 主要包括主干网络改进、轻量化网络、注意力机制、检测头部改进、空间金字塔池化、损失函数及NMS改进、ICCV/CVPR/ECCV视觉顶会创新点改进、各类数据集资源分享以及算法训练相…