java抽奖系统登录下(四)

6.4 关于登录

最简单的登录:

        1、web登录页填写登录信息,前端发送登录信息到后端;

        2、后端接受登录信息,并校验。校验成功,返回成功结果。

        这种登录会出现一个问题,用户1成功登录之后,获取到后台管理页,将这个页面的url分享给用户2,用户2没有进行登录就会直接进入到后台管理页,容易造成信息泄露;

        解决方法:用户在进入后台管理页之前要进行校验;

几种常见的登录验证的方式:

方式1、cookie-session

1、用户带登录的时候,就会把用户信息发送到后端,后端根据用户信息创建一个sessionid,将用户登录的相关信息放入到session中,后续通过sessionid来进行查找校验,并将sessionid传送到前端;

2、前端接收到sieeionid之后将其存入到cookie中,后续在进行登陆之后会携带cookie,后端会根据前端发送过来的cookie解析到其中的sieeionid,通过sessionid来查找是否用这个id所对应的用户消息。如果存在用户信息,则表示之前这个用户登录过,就会放心该用户跳转到其他界面;如果该id没有查找到相关的用户信息,则表示该用户之前没有进行登录过,不会放行;

缺点:1、cookie只在web网页中生成,存在环境限制;

        2、这种方式不能跨域;

        3、cookie只能在本机保存,不能在集群环境中使用;

方式2: token认证

        上图的token是存储在redis,由于redis是可以部署在集群环境中,所以解决了cookie-session的一下缺陷;

        缺点:用户数量大会导致频繁的访问redis校验token,对于内存来说有很大的压力。

方式三:基于token的jwt令牌认证

 

        1、前端的登录信息发送到后端之后,后端基于jwt服务产生一个string类型的字符串,成为jwt令牌,这个令牌是不需要存储在后端的,而是直接返回到前端;

        2、前端使用本地的存储方式,将jwt存储起来,后续在进行操作的话会带着jwt令牌到后端,后端会对jwt令牌进行校验,如果能够正确使用jwt解密,说明校验通过了;

        jwt:实际上是一个加解密的过程,这个过程是依靠jwt独立的工具包来完成和实现的。

        jwt:结构是由负载,签名和头部组成的,由这三部分组成一个串;

6.5 jwt令牌工具类的实现

        该工具类主要完成jwt的加密和解密操作:

 首先引入相关的依赖:

新建jwtutil类:

public class JWTUtil {
    private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);
    /**
     * 密钥:Base64编码的密钥
     */
    private static final String SECRET = "weS2l8Tp9wDFov9ic72l/9VRT3j9aYfhEfi8qwGMDgU=";
    /**
     * 生成安全密钥:将一个Base64编码的密钥解码并创建一个HMAC SHA密钥。
     */
    private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(
            Decoders.BASE64.decode(SECRET));
    /**
     * 过期时间(单位: 毫秒)
     */
    private static final long EXPIRATION = 60*60*1000*24*30;//一个月

    /**
     * 生成密钥
     *
     * @param claim  {"id": 12, "name":"张山"}
     * @return
     */
    public static String genJwt(Map<String, Object> claim){
        //签名算法
        String jwt = Jwts.builder()
                .setClaims(claim)             // 自定义内容(载荷)
                .setIssuedAt(new Date())      // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 设置过期时间
                .signWith(SECRET_KEY)         // 签名算法,和加解密相关
                .compact();
        return jwt;
    }

    /**
     * 验证密钥
     */
//    Claims是jwt里面定义的对象
    public static Claims parseJWT(String jwt){
        if (!StringUtils.hasLength(jwt)){
            return null;
        }
        // 创建解析器, 设置签名密钥
        JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder().setSigningKey(SECRET_KEY);
        Claims claims = null;
        try {
            //解析token
            claims = jwtParserBuilder.build().parseClaimsJws(jwt).getBody();
        }catch (ExpiredJwtException e){
            // 签名验证失败
//            logger.error("解析令牌错误,jwt:{}", jwt, e);
            claims = e.getClaims();
        }
        return claims;

    }

    /**
     * 从token中获取用户ID
     */
    public static Integer getUserIdFromToken(String jwtToken) {
        Claims claims = JWTUtil.parseJWT(jwtToken);
        if (claims != null) {
            Map<String, Object> userInfo = new HashMap<>(claims);
            return (Integer) userInfo.get("userId");
        }
        return null;
    }
}

controller层登录接口设计:

    @RequestMapping("/password/login")
    public CommonResult<UserLoginResult> userPasswordLogin(
            @Validated @RequestBody UserPasswordLoginParam param) {
        logger.info("userPasswordLogin UserPasswordLoginParam:{}",
                JacksonUtil.writeValueAsString(param));
        UserLoginDTO userLoginDTO = userService.login(param);
        return CommonResult.success(convertToUserLoginResult(userLoginDTO));
    }

 service层登录接口实现:

@Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO;
        // 类型检查与类型转换,java 14及以上版本
        if (param instanceof UserPasswordLoginParam loginParam) {
            // 密码登录流程
            userLoginDTO = loginByUserPassword(loginParam);
        } else if (param instanceof ShortMessageLoginParam loginParam) {
            // 短信验证码登录流程
            userLoginDTO = loginByShortMessage(loginParam);
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }

        return userLoginDTO;
    }

    private UserLoginDTO loginByShortMessage(ShortMessageLoginParam loginParam) {
        if (!RegexUtil.checkMobile(loginParam.getLoginMobile())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        // 获取用户数据
        UserDO userDO = userMapper.selectByPhoneNumber(
                new Encrypt(loginParam.getLoginMobile()));
        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                && !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        // 校验验证码
        String code = verificationCodeService.getVerificationCode(loginParam.getLoginMobile());
        if (!loginParam.getVerificationCode().equals(code)) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }
        // 塞入返回值(JWT)
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", userDO.getId());
        claim.put("identity", userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    private UserLoginDTO loginByUserPassword(UserPasswordLoginParam loginParam) {
        UserDO userDO = null;
        // 判断手机登录还是邮箱登录
        if (RegexUtil.checkMail(loginParam.getLoginName())) {
            // 邮箱登录
            // 根据邮箱查询用户表
            userDO = userMapper.selectByMail(loginParam.getLoginName());
        } else if (RegexUtil.checkMobile(loginParam.getLoginName())) {
            // 手机号登录
            // 根据手机号查询用户表
            userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXIST);
        }

        // 校验登录信息
        if (null == userDO) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                && !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            // 强制身份登录,身份校验不通过
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {
            // 校验密码不同
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 塞入返回值(JWT)
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", userDO.getId());
        claim.put("identity", userDO.getIdentity());
        String token = JWTUtil.genJwt(claim);
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

使用postman对登录进行测试:

账号密码:

验证码登录:

 开启redis缓存验证码:service redis-server start

发送验证码:

验证码登录:

6.6 前端登录完善

进行前端测试:

密码登录:

短信验证码登录:

登录成功之后进入新的界面:

在登录页面点击注册进入注册见面,注册成功之后返回登录界面:

6.7 登录拦截器

        在设置好jwt令牌登陆之后,用户进行登录操作,会得到后端传过来的jwt令牌,其次用户会拿着这个令牌去访问其他界面的时候,后端会对这个jwt令牌进行校验,这里采用的是拦截器,当然不是所有的请求都需要校验,会进行相关配置;

登录拦截器:

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    /**登录拦截器
     * 预处理,业务请求之前调用
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 获取请求头,jwt是在request的请求头中的
        String token = request.getHeader("user_token");
        log.info("获取token:{}", token);
        log.info("获取路径:{}", request.getRequestURI());
        // 令牌解析
        Claims claims = JWTUtil.parseJWT(token);
        if (claims == null) {
            log.error("解析JWT令牌失败!");
            response.setStatus(401);
            return false;
        }
        log.info("解析JWT令牌成功!放行");
        return true;
    }
}

配置登录拦截资源:

@Configuration
public class AppConfig implements WebMvcConfigurer {
    //配置项
    @Autowired
    private LoginInterceptor loginInterceptor;
    private final List<String> excludes = Arrays.asList(
            "/**/*.html",
            "/css/**",
            "/js/**",
            "/pic/**",
            "/*.jpg",
            "/*.png",
            "/favicon.ico",
            "/**/login",
            "/register",
            "/verification-code/send"
    );
    @Override
    //添加自定义的登录拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excludes);
    }
}

ps:关于登录的内容就到这里了,谢谢观看!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面经zijie

以下是对 C# GC 和 Lua GC 的详细分析&#xff0c;包括它们的原理、特性、优化方式及对比。 C# GC&#xff1a;详细分析 C# 的垃圾回收器 (Garbage Collector, GC) 是一个自动内存管理系统&#xff0c;它在程序运行时负责管理对象的分配和释放&#xff0c;防止内存泄漏。 1. …

利用代理IP爬取Zillow房产数据用于数据分析

引言 最近数据分析的热度在编程社区不断攀升&#xff0c;有很多小伙伴都开始学习或从事数据采集相关的工作。然而&#xff0c;网站数据已经成为网站的核心资产&#xff0c;许多网站都会设置一系列很复杂的防范措施&#xff0c;阻止外部人员随意采集其数据。为了解决这个问题&a…

海康萤石摄像机接入EasyNVR流程:开启RTSP-》萤石视频添加到EasyNVR-》未来支持海康SDK协议添加到EasyNVR

EasyNVR目前支持GB28181、RTSP、ONVIF、RTMP&#xff08;推流&#xff09;这几种协议接入&#xff0c;目前正在增加海康HIKSDK、大华DHSDK等几种SDK的接入&#xff0c;我们今天就介绍一下萤石摄像机怎么通过RTSP接入到EasyNVR。 第一步&#xff1a;萤石摄像机开启 萤石设备默…

Pytest-Bdd-Playwright 系列教程(14):Docstring 参数

Pytest-Bdd-Playwright 系列教程&#xff08;14&#xff09;&#xff1a;Docstring 参数 前言一、什么是docstring?二、基本语法三、主要特点四、实际例子五、注意事项六、使用建议总结 前言 在自动化测试的过程中&#xff0c;我们经常需要处理复杂的测试数据或需要输入多行文…

Quad Remesher使用教程

为什么要拓扑&#xff1f; 我们知道&#xff0c;模型在三维软件中的表现&#xff0c;是由一系列的面通过不同角度组合而成的。3D模型制作层面上的拓扑&#xff0c;按我的理解来说&#xff0c;就是一个模型的面的结构分布——布线。想表现和制作一个三维模型&#xff0c;有无限…

智慧政务数据中台建设及运营解决方案

数据中台&#xff1a;政府数字化转型的引擎 数据中台作为政府数字化转型的核心驱动力&#xff0c;起源于美军的作战体系&#xff0c;强调高效、灵活与强大。它不仅促进了政府决策的科学性&#xff0c;还推动了政府服务的精细化与智能化。 数据中台的应用场景&#xff1a;数字…

Transformer: Attention Is All You Need (2017) 翻译

论文&#xff1a;Attention Is All You Need 下载地址如下: download: Transformer Attention Is All you need Attention Is All You Need 中文 《Attention Is All You Need》是《Transformer》模型的开创性论文&#xff0c;提出了一种全新的基于注意力机制的架构&#xf…

Android 系统应用重名install安装失败分析解决

Android 系统应用重名install安装失败分析解决 文章目录 Android 系统应用重名install安装失败分析解决一、前言1、Android Persistent apps 简单介绍 二、系统 persistent 应用直接安装需求分析解决1、系统应用安装报错返回的信息2、分析解决 三、其他1、persistent系统应用in…

3D一览通在线协同设计,助力汽车钣金件设计与制造数字化升级

汽车行业已迎来智能化的汹涌浪潮&#xff0c;在此背景下&#xff0c;零部件制造商唯有积极应对&#xff0c;以智能制造为核心驱动力&#xff0c;方能跟上行业发展步调&#xff0c;在激烈的市场竞争中抢占先机。作为整车制造不可或缺的核心组件之一&#xff0c;汽车钣金件亦需紧…

如何将你的 Ruby 应用程序从 OpenSearch 迁移到 Elasticsearch

作者&#xff1a;来自 Elastic Fernando Briano 将 Ruby 代码库从 OpenSearch 客户端迁移到 Elasticsearch 客户端的指南。 OpenSearch Ruby 客户端是从 7.x 版 Elasticsearch Ruby 客户端分叉而来的&#xff0c;因此代码库相对相似。这意味着当将 Ruby 代码库从 OpenSearch 迁…

Kafka系列教程 - Kafka 生产者 -2

1. 生产者简介 不管是把 Kafka 作为消息队列系统、还是数据存储平台&#xff0c;总是需要一个可以向 Kafka 写入数据的生产者和一个可以从 Kafka 读取数据的消费者&#xff0c;或者是一个兼具两种角色的应用程序。 使用 Kafka 的场景很多&#xff0c;诉求也各有不同&#xff…

动态规划:0-1背包问题 图文+举例超详细说明

一、题目描述 给定n(n<100)种物品和一个背包。物品i的重量是wi(wi<100)&#xff0c;价值为vi(vi<100)&#xff0c;背包的容量为C(C<1000)。 应如何选择装入背包中的物品&#xff0c;使得装入背包中物品的总价值最大? 在选择装入背包的物品时&#xff0c;对每种物…

实例:图片处理

目录 图片处理 Python代码展示 代码逐行注释 图片素材 运行结果 需要注意的几点&#xff1a; 运行思路 1. 导入必要的模块及类&#xff08;开头部分&#xff09; 2. 定义文件相似度检查函数&#xff08;file_similarity_checker 函数部分&#xff09; 3. 指定要比较的…

鸿蒙项目云捐助第四讲鸿蒙App应用的登陆注册页实现

根据app的操作流程可以知道&#xff0c;当启动页启动后&#xff0c;点击启动页中的页面就进入到了登录页。本讲就是针对于登录注册页的实现&#xff0c;实现的界面参考下图。 这里根据这个素材的参考实现鸿蒙Next云捐助的登录页。 一、鸿蒙Next云捐助登录页的实现 在项目中继…

大屏开源项目go-view二次开发1----环境搭建(C#)

最近公司要求做一个大屏的程序用于展示公司的产品&#xff0c;我以前也没有相关的经验&#xff0c;最糟糕的是公司没有UI设计的人员&#xff0c;领导就一句话要展示公司的产品&#xff0c;具体展示的内容细节也不知道&#xff0c;全凭借自己发挥。刚开始做时是用wpf做的&#x…

WHLUG丨deepin、华中科技大学开放原子开源俱乐部、 RustSBI 和清华大学开源操作系统训练营共话开源新生代成长之路

2024年11月30日下午&#xff0c;由 deepin&#xff08;深度&#xff09;社区联合华中科技大学开放原子开源俱乐部、 RustSBI 开源社区和清华大学开源操作系统训练营共同举办的WHLUG&#xff08;武汉Linux用户组&#xff09;线下沙龙在华中科技大学成功举办。 本次活动聚集了50余…

操作系统的基本认识

操作系统的感性认识 操作系统这个词可能或多或少听说过&#xff0c;比如windows, linux, macOS。这些其实都是工程师们经过实践后的具象化产物。而操作系统原理这六个字就是操作系统的抽象化&#xff0c;更准确的说&#xff0c;操作系统原理是很理论化的东西。举一个不是很恰当…

强化学习Q-learning及其在机器人路径规划系统中的应用研究,matlab代码

一、Q-learning 算法概述 Q-learning 是一种无模型的强化学习算法&#xff0c;它允许智能体&#xff08;agent&#xff09;在没有环境模型的情况下通过与环境的交互来学习最优策略。Q-learning的核心是学习一个动作价值函数&#xff08;Q-function&#xff09;&#xff0c;该函…

微信小程序横屏页面跳转后,自定义navbar样式跑了?

文章目录 问题原因&#xff1a;解决方案&#xff1a; 今天刚遇到的问题&#xff0c;横屏的页面完成操作后跳转页面后&#xff0c;自定义的tabbar样式乱了&#xff0c;跑到最顶了&#xff0c;真机调试后发现navbar跑到手机状态栏了&#xff0c;它正常应该跟右边胶囊一行。 知道问…

分布式 Paxos算法 总结

前言 相关系列 《分布式 & 目录》《分布式 & Paxos算法 & 总结》《分布式 & Paxos算法 & 问题》 参考文献 《图解超难理解的 Paxos 算法&#xff08;含伪代码&#xff09;》《【超详细】分布式一致性协议 - Paxos》 Basic-Paxos 基础帕克索斯算法…