【用户登录】模块之登录认证+鉴权业务逻辑

news2025/2/22 1:07:51

用户登录——⭐认证功能的流程图:


⭐鉴权流程图:


用户登录功能的Java代码实现

1. 实体类-User

orm框架:JPA

@Table(name = "user_tab")
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="user_id")
    private Long id;

    @Column(name="user_name")
    private String username;

    @Column(name="user_password")
    private String password;

    @Column(name="user_phone")
    private String phone;

    @Column(name="user_nickname")
    private String nickname;

    @Column(name="user_create_by")
    private String createBy;
    @Column(name="user_create_time")
    private Date createTime;
    @Column(name="user_update_time")
    private Date updateTime;

    @Column(name="user_role_id")
    private Long roleId;
}

2. UserDao

@Repository
public interface UserDao extends JpaRepository<User,Long> {

    //根据user的username和password查询该用户
    User findByUsernameAndPassword(String username,String password);
}

3. UserService业务层接口

public interface UserService {

    //全查询
    List<User> findAllUsers();

    //1027-【从数据库读取用户名信息存入布隆过滤器中】
    void warnUpUsernames();

    //1027-【用户登录】
    User login(String username, String password);

}

4. ⭐UserServiceImpl业务实现类

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //全查询
    @Override
    public List<User> findAllUsers() {
        return userDao.findAll();
    }

    //1027-【从数据库读取用户名信息存入布隆过滤器中】
    @Override
    public void warnUpUsernames() {
        userDao.findAll().forEach(u -> {
                    stringRedisTemplate.opsForValue()
                            .getOperations()
                            .execute(new DefaultRedisScript<Long>(
                                            "return redis.call('bf.add',KEYS[1],ARGV[1])"
                                            , Long.class),
                                    new ArrayList<String>() {{
                                        add("whiteUsernames");
                                    }}, u.getUsername()
                            );
                    }
        );
    }

    //1027-【用户登录】
    @Override
    public User login(String username, String password) {
        if(!StringUtils.hasText(username)){
            throw new UsernameIsEmptyException("用户名为空异常");
        }
        username = username.trim();
        if(checkFromWhite(username)){
            throw new UsernameNotFoundException("用户名不存在异常");
        }

        User user = userDao.findByUsernameAndPassword(username, password);

        if(Objects.isNull(user)){
            throw  new BadCredentialsException("用户名|密码错误");
        }

        return user;
    }

    //判断用户名是否存在于布隆过滤器
    private boolean checkFromWhite(String username) {
        Long isExist = stringRedisTemplate.opsForValue()
                .getOperations()
                .execute(new DefaultRedisScript<Long>("return redis.call('bf.exists',KEYS[1],ARGV[1])", Long.class),
                        new ArrayList<String>() {{
                            add("whiteUsernames");
                        }}, username);

        return isExist.intValue() == 0;
    }

5. ⭐UserController控制层接口

@Api(tags = "用户模块接口")
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    //1027-【全查询】
    @ApiOperation(value = "findAllUsers",notes = "查询所有用户,需要当前用户登录状态")
    @GetMapping("/findAllUsers")
    @BmsRole(value="1")
    public HttpResp<List<User>> findAllUsers(){
        return HttpResp.success(userService.findAllUsers());
    }


    //1027-【用户登录】
    @ApiOperation(value = "login",notes = "用户登录")
    @GetMapping("login")
    public HttpResp login(HttpServletResponse response, String username, String password){
        //首先调用AuthorityService的login方法进行用户登录验证,返回一个User对象。
        User user = userService.login(username,password);

        //然后生成一个JWT作为用户的身份认证凭证,其中包含了用户名和过期时间等信息。
        //使用JWT.create()创建JWT对象,并使用withClaim()方法设置用户名,withExpiresAt()方法设置过期时间。
        String salt = Base64.getEncoder().encodeToString((username+":"+password).getBytes(StandardCharsets.UTF_8));


        log.debug("user:{}",user);
        String token = JWT.create()
                .withClaim("username", username)
                .withClaim("roleId",""+user.getRoleId())
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 30))   //30分钟令牌过期
                .sign(Algorithm.HMAC256(salt)); //使用Algorithm.HMAC256(salt)指定加密算法和密钥,对JWT进行签名。
        log.debug("用户验证通过,生成token为:{}",token);

        //将生成的JWT存储到Redis缓存中,使用stringRedisTemplate.opsForValue().set()方法设置键值对,并使用stringRedisTemplate.expire()方法设置过期时间
        stringRedisTemplate.opsForValue().set(token,salt);
        stringRedisTemplate.expire(token,60, TimeUnit.MINUTES); //60分钟redis缓存token过期
        response.addCookie(new Cookie("token",token));  //将JWT作为Cookie添加到HTTP响应中,使用response.addCookie()方法

        return HttpResp.success(user);  //最后返回一个成功的响应,消息体中包含了登录成功的用户对象
    }

}

用户认证解决方案

1. 配置部署拦截器

@Configuration
@Slf4j
public class BmsMvcConfig implements WebMvcConfigurer {

    @Bean
    public AuthorityInterceptor authorityInterceptor(){
        log.debug("BmsMvc拦截器启动成功:{}..........",new Date());
        return new AuthorityInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authorityInterceptor())
                .addPathPatterns("/api/**")
                .excludePathPatterns(
                        "/api/user/login"
                );
    }
}

2. ⭐自定义拦截器

@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     *
     * @param request
     * @param response
     * @param handler   当前拦截器拦截的方法
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("已进入拦截器:{}",new Date());

        String token = request.getHeader("token");

        //1.从redis中读取token,如果不存在,则用户是非法用户,抛出自定义异常类InvalidTokenException
        Boolean isRedis = stringRedisTemplate.hasKey(token);

        if(!isRedis) throw new InvalidTokenException("无效的token");

        String salt = stringRedisTemplate.opsForValue().get(token);
        log.debug("salt:{}",salt);
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(salt)).build().verify(token);

        //2.token验证完成正确
        String roleId = decodedJWT.getClaim("roleId").asString();
        log.debug("------->roleId:{}",roleId);

        HandlerMethod method = (HandlerMethod) handler;
        //System.out.println("----->"+method.getMethod().getDeclaredAnnotation(GetMapping.class));
        BmsRole bmsRole= method.getMethod().getDeclaredAnnotation(BmsRole.class);
        String requiredRolId = bmsRole.value();

        if(!roleId.equals(requiredRolId)){
            throw new PermissionDeniedException("您没有足够的权限");
        }

        //获取请求对象的角色名称
        //获取请求的地址(uri)
//        String requestURI = request.getRequestURI();
//        requestURI = requestURI.substring(requestURI.lastIndexOf("/") + 1);
//        log.debug("请求路径uri:{}",requestURI);//URL/URI
//        System.out.println(handler.getClass());
        
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

3. 在spring.factories文件加载部署

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.****.bms.authority.config.BmsMvcConfig,com.****.bms.authority.handler.UserExceptionHandler

未完待续......

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

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

相关文章

果蔬购物商城管理与推荐系统Python+Django网页界面+协同过滤推荐算法

一、介绍 果蔬购物管理与推荐系统。本系统以Python作为主要开发语言&#xff0c;前端通过HTML、CSS、BootStrap等框架搭建界面&#xff0c;后端使用Django框架作为逻辑处理&#xff0c;通过Ajax实现前后端的数据通信。并基于用户对商品的评分信息&#xff0c;采用协同过滤推荐…

机器学习---使用 TensorFlow 构建神经网络模型预测波士顿房价和鸢尾花数据集分类

1. 预测波士顿房价 1.1 导包 from __future__ import absolute_import from __future__ import division from __future__ import print_functionimport itertoolsimport pandas as pd import tensorflow as tftf.logging.set_verbosity(tf.logging.INFO) 最后一行设置了Ten…

Spring Security获得认证流程解析(示意图)

建议先看完Spring Security总体架构介绍和Spring Security认证架构介绍&#xff0c;然后从FilterChainProxy的doFilterInternal函数开始&#xff0c;配合文章进行debug以理解Spring Security认证源码的执行流程。 在之前的Spring Security认证架构介绍中&#xff0c;我们已经知…

一文详解汽车电子CAN总线

0.什么是CAN总线 CAN总线(控制器区域网络)是一个中央网络系统&#xff0c;连接不同的电子控制单元(ECU)以及车辆中的其他设备。现在的汽车可以有100个ECU&#xff0c;因此CAN总线通信变得非常重要。 1.CAN总线流行的背景 集中式:CAN总线系统允许对连接到网络的ECU进行集中控制…

Redis快速上手篇七(集群-一台虚拟机六个节点)

​​​​​​http://t.csdnimg.cn/S0NpK与上篇六个虚拟机配置基本一样有不懂可以看上篇配置实例 集群搭建 根据上篇文章&#xff0c;本篇只着重于小方面的配置差别 配置集群一般不要设置密码 1.搭建一台虚拟机后再安装目录下新建文件夹 redis_cluster 2.在文件夹内创建六个文…

python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解

这篇文章主要介绍了python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧 JDBC Request 这个 Sampler 可以向数据库…

C# 图解教程 第5版 —— 第11章 结构

文章目录 11.1 什么是结构11.2 结构是值类型11.3 对结构赋值11.4 构造函数和析构函数11.4.1 实例构造函数11.4.2 静态构造函数11.4.3 构造函数和析构函数小结 11.5 属性和字段初始化语句11.6 结构是密封的11.7 装箱和拆箱&#xff08;*&#xff09;11.8 结构作为返回值和参数11…

AK F.*ing leetcode 流浪计划之delaunay三角化

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 本期话题&#xff1a;给定二维点进行delaunay三角化 参考资料&#xff1a; 算法步骤与框架&#xff1a; https://oi-wiki.org//geometry/triangulation/ 空圆性深入解…

黑客技术(网络安全)—小白自学

目录 一、自学网络安全学习的误区和陷阱 二、学习网络安全的一些前期准备 三、网络安全学习路线 四、学习资料的推荐 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类&am…

本来打算做功能测试的,但是发现playwright太好玩了,玩了一天,功能测试进度为空

本文是作者的自言自语&#xff1a;//todo 未完待续 https://blog.csdn.net/lineuman 微软果然有大牛啊&#xff01;有能人的公司总是令人敬佩。 playwright这种级别的工具简直就是核弹级别的。 当我开始使用playwright的时候&#xff0c;嘭的一下&#xff0c;我的世界炸了&…

ResNet(CVPR2016)

文章目录 AbstractIntroductionRelated WorkResidual RepresentationsShortcut Connections Deep Residual LearningResidual LearningIdentity Mapping by Shortcuts ExperimentConclusion 原文链接 Abstract 深层的神经网络更难训练&#xff0c;我们提出了一个残差学习框架&…

数据结构与算法--复杂度

目录 1.算法效率 1.1 如何衡量一个算法的好坏 1.2 算法的复杂度 1.3 复杂度在校招中的考察 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算举例 3.空间复杂度 4. 常见复杂度对比 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的…

VirtualBox网络配置

1. 进入虚拟机所在系统的网络设置 2. 网卡1连接方式选择为仅主机网络&#xff0c;界面名称选择自带的网卡 3.自带网卡的配置方式&#xff0c;通常已经配置好了&#xff0c;保持dhcp开启即可 4.网卡2选择nat转换即可&#xff0c;无需添加其他配置 5.启动虚拟机所在系统&#xff…

数组OJ题汇总(一)

本专栏内容为&#xff1a;leetcode刷题专栏&#xff0c;记录了leetcode热门题目以及重难点题目的详细记录 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;Leetcode &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &…

MSQL系列(十) Mysql实战-Join驱动表和被驱动表区分

Mysql实战-Join驱动表和被驱动表区分 前面我们讲解了Mysql的查询连接Join的算法原理, 我发现大家都知道小表驱动大表,要让小表作为驱动表, 现在有2个问题 查询多表, 到底哪个是驱动表?哪个是被驱动表, 如何区分?索引如何优化,到底是加在驱动表上,还是被驱动表上? 今天我们…

[Leetcode] 0108. 将有序数组转换为二叉搜索树

108. 将有序数组转换为二叉搜索树 题目描述 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&#xff1a…

【Android内存优化】内存泄露优化之强引用变弱引用完全详解

内存泄露背景 什么是内存泄露 内存空间使用完毕后无法被释放的现象,对于还保持着引用, 该内存不能再被分配使用,逻辑上却已经不会再用到的对象,垃圾回收器不会回收它们。 所以逻辑不再使用的对象,需要释放强引用,以便GC进行回收。 JVM 工作原理 JVM 垃圾回收原理,点…

vlc打开网络流(如rtmp),并查看媒体信息(如编码格式等编码信息)

打开vlc 选择媒体&#xff0c;打开网络串流 输入rtmp地址&#xff0c;点击播放 选择工具-编解码信息 可以查看节目的编码信息什么的

HBuilderX代码变量名称翻译插件

对于许多开发者而言&#xff0c;怎么规范的命名变量是一个非常痛苦的事&#xff0c;而在HBuilderX中有一个的插件可以快速的帮助你完成中文转带格式的变量名&#xff0c;格式可以选择小驼峰、大驼峰、下划线、常量、CSS类名等。 以下为添加此插件的步骤 1、打开插件安装 选择…

Android 类似淘宝的吸顶特效 NestedScrollView+RecycleView

运行图 布局的设计 要实现上面的效果需要搞定NestedScrollView和RecycleView的滑动冲突。有人要问RecycleView为何要滑动自动撑大不就好了么&#xff1f;这个问题其实对于有限的资源加载来说是很好的解决方案&#xff0c;但是如果涉及到的是图文结合的并且有大批量的数据的时候…