Spring Boot+Vue前后端分离项目练习02之网盘项目利用token进行登陆验证

news2024/11/16 3:38:15

1.添加依赖

首先需要添加jwt对应的依赖。

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

2.添加配置

JWT由三部分构成,分别是 header, payload 和 signature, 其中前两部分中的参数涉及到的一些参数需要用户自定义,因此我们需要将这些参数放到配置文件中,这样后续就可以动态修改配置。

# 密钥
jwt.secret = 6L6T5LqG5L2g77yM6LWi5LqG5LiW55WM5Y+I6IO95aaC5L2V44CC
# 签名算法:HS256,HS384,HS512,RS256,RS384,RS512,ES256,ES384,ES512,PS256,PS384,PS512
jwt.header.alg = HS256
#jwt签发者
jwt.payload.iss = picacho
#jwt过期时间(单位:毫秒)
jwt.payload.exp = 60 * 60 * 1000 * 24 * 7
#jwt接收者
jwt.payload.aud = picacho_1

3.创建JWT配置类

创建config包,在该包下创建JWT对应的配置类。

import lombok.Data;

@Data
public class JwtHeader {
    private String alg;
    private String typ;
}
import lombok.Data;

@Data
public class JwtPayload {
    private String iss;
    private String exp;
    private String sub;
    private String aud;
}
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
    private String secret;
    private JwtHeader header;
    private JwtPayload payload;
}

这里通过配置文件的属性值来为jwt进行属性值的配置。

4.创建JWT工具类

创建JWTUtil类,在里面创建generalSecretKey方法来生成密钥,生成的过程需要使用 JWT的方法SecretKeySpec来生成密钥,该密钥在创建JWT和验证 JWT 的时候都会用到且相同。

@Component
public class JWTUtil {

    @Resource
    JwtProperties jwtProperties;

    /**
     * 由字符串生成加密key
     * @return
     */
    private SecretKey generalKey() {
        // 本地的密码解码
        byte[] encodedKey = Base64.decodeBase64(jwtProperties.getSecret());
        // 根据给定的字节数组使用AES加密算法构造一个密钥
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

创建生成JWT方法。

    /**
     * 创建jwt
     * @param subject
     * @return
     * @throws Exception
     */
    public String createJWT(String subject) throws Exception {

        // 生成JWT的时间
        long nowTime = System.currentTimeMillis();
        Date nowDate = new Date(nowTime);
        // 生成签名的时候使用的秘钥secret
        SecretKey key = generalKey();

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine se = manager.getEngineByName("js");
        int expireTime = 0;
        try {
            expireTime =(int) se.eval(jwtProperties.getPayload().getExp());
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        // 为payload添加各种标准声明和私有声明
        DefaultClaims defaultClaims = new DefaultClaims();
        defaultClaims.setIssuer(jwtProperties.getPayload().getIss());
        defaultClaims.setExpiration(new Date(System.currentTimeMillis() + expireTime));
        defaultClaims.setSubject(subject);
        defaultClaims.setAudience(jwtProperties.getPayload().getAud());

        JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder,设置jwt的body
                .setClaims(defaultClaims)
                .setIssuedAt(nowDate) // iat(issuedAt):jwt的签发时间
                .signWith(SignatureAlgorithm.forName(jwtProperties.getHeader().getAlg()), key); // 设置签名,使用的是签名算法和签名使用的秘钥

        return builder.compact();
    }

创建解密JWT方法。

    /**
     * 解密jwt
     * @param jwt
     * @return
     * @throws Exception
     */
	public Claims parseJWT(String jwt) throws Exception {
		SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
		Claims claims = Jwts.parser() // 得到DefaultJwtParser
				.setSigningKey(key) // 设置签名的秘钥
				.parseClaimsJws(jwt).getBody(); // 设置需要解析的jwt
		return claims;
	}

5.实现用户注册

5.1 实现业务层

创建service包,在该包下创建UserService.java类,实现用户注册逻辑。

import com.picacho.common.RestResult;
import com.picacho.entity.User;

public interface UserService {
    RestResult<String> registerUser(User user);
}

创建impl包,在该包下创建其实现类。

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

    @Resource
    UserMapper userMapper;

    @Override
    public RestResult<String> registerUser(User user) {
        //判断验证码
        String telephone = user.getTelephone();
        String password = user.getPassword();

        if (!StringUtils.hasLength(telephone) || !StringUtils.hasLength(password)){
            return RestResult.fail().message("手机号或密码不能为空!");
        }
        if (isTelePhoneExit(telephone)){
            return RestResult.fail().message("手机号已存在!");
        }

        String salt = UUID.randomUUID().toString().replace("-", "").substring(15);
        String passwordAndSalt = password + salt;
        String newPassword = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());

        user.setSalt(salt);

        user.setPassword(newPassword);
        user.setRegisterTime(DateUtil.getCurrentTime());
        int result = userMapper.insert(user);

        if (result == 1) {
            return RestResult.success();
        } else {
            return RestResult.fail().message("注册用户失败,请检查输入信息!");
        }
    }

    /**
     * 判断手机号是否存在
     * @param telePhone
     * @return
     */
    private boolean isTelePhoneExit(String telePhone) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getTelephone, telePhone);
        List<User> list = userMapper.selectList(lambdaQueryWrapper);
        if (list != null && !list.isEmpty()) {
            return true;
        } else {
            return false;
        }
    }
}

5.2 创建DTO类

在前后端分离项目中,一般前端与后端需要统一数据交互;一般做法是创建DTO类,例如注册功能,这里可以创建RegisterDTO.java,其属性就是前端需要传给后端的数据。

创建dto包,在该包下创建RegisterDTO.java。

import lombok.Data;

@Data
public class RegisterDTO {
    private String username;
    private String telephone;
    private String password;
}

5.3 添加注册接口

    @Resource
    UserService userService;


    @PostMapping(value = "/register")
    @ResponseBody
    public RestResult<String> register(@RequestBody RegisterDTO registerDTO) {
        RestResult<String> restResult = null;
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setTelephone(registerDTO.getTelephone());
        user.setPassword(registerDTO.getPassword());

        restResult = userService.registerUser(user);

        return restResult;
    }

5.4 测试效果

启动项目测试效果:
在这里插入图片描述
在这里插入图片描述

6.实现用户登陆

6.1 实现业务层

RestResult<User> login(User user);
    @Override
    public RestResult<User> login(User user) {
        
        String telephone = user.getTelephone();
        String password = user.getPassword();

        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getTelephone, telephone);
        User saveUser = userMapper.selectOne(lambdaQueryWrapper);
        String salt = saveUser.getSalt();
        String passwordAndSalt = password + salt;
        String newPassword = DigestUtils.md5DigestAsHex(passwordAndSalt.getBytes());
        if (newPassword.equals(saveUser.getPassword())) {
            saveUser.setPassword("");
            saveUser.setSalt("");
            return RestResult.success().data(saveUser);
        } else {
            return RestResult.fail().message("手机号或密码错误!");
        }

    }

6.2 创建VO类

import lombok.Data;

@Data
public class LoginVO {
    private String username;
    private String token;
}

6.3 添加登陆接口

    @Resource
    JWTUtil jwtUtil;

    @GetMapping(value = "/login")
    @ResponseBody
    public RestResult<LoginVO> userLogin(String telephone, String password) {
        RestResult<LoginVO> restResult = new RestResult<LoginVO>();

        LoginVO loginVO = new LoginVO();
        User user = new User();
        user.setTelephone(telephone);
        user.setPassword(password);
        RestResult<User> loginResult = userService.login(user);

        if (!loginResult.getSuccess()) {
            return RestResult.fail().message("登录失败!");
        }

        loginVO.setUsername(loginResult.getData().getUsername());
        String jwt = "";
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            jwt = jwtUtil.createJWT(objectMapper.writeValueAsString(loginResult.getData()));
        } catch (Exception e) {
            return RestResult.fail().message("登录失败!");
        }
        loginVO.setToken(jwt);

        return RestResult.success().data(loginVO);
    }

6.4 测试效果

启动项目测试效果:
在这里插入图片描述
这里返回了token,后面就可以通过携带token来访问需要登陆才能有权限访问的路径。

7.验证token

    @GetMapping("/checkToken")
    @ResponseBody
    public RestResult<User> checkToken(@RequestHeader("token") String token) {
        RestResult<User> restResult = new RestResult<User>();
        User tokenUserInfo = null;
        try {
            Claims c = jwtUtil.parseJWT(token);
            String subject = c.getSubject();
            ObjectMapper objectMapper = new ObjectMapper();
            tokenUserInfo = objectMapper.readValue(subject, User.class);

        } catch (Exception e) {
            return RestResult.fail().message("认证失败");
        }
        if (tokenUserInfo != null) {
            return RestResult.success().data(tokenUserInfo);
        } else {
            return RestResult.fail().message("用户暂未登录");
        }
    }

7.1 测试效果

启动项目,携带token测试效果。
在这里插入图片描述
可以看到验证成功了。

项目demo源码下载地址:源码下载

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

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

相关文章

详解数据结构中的顺序表的手动实现,顺序表功能接口【数据结构】

文章目录线性表顺序表接口实现尾插尾删头插头删指定位置插入指定位置删除练习线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列…

Freemarker动态模板渲染flyingsaucer将html转PDF(多页固定头尾)

目录一、序言二、CSS样式控制打印模板三、代码示例1、pom.xml2、application.yml3、PdfGenerationController4、Freemarker模板内容四、展示效果一、序言 一般正常来说&#xff0c;生成PDF的操作都是通过将HTML转成PDF&#xff0c;HTML动态渲染可以借助模板引擎&#xff0c;如…

从外行到外包,从手工测试到知名互联大厂测开,我经历了什么...

本人本科就读于某普通一本院校&#xff08;非985&#xff0c;211&#xff09;&#xff0c;经管类专业&#xff0c;从大四实习到15年毕业后前两年一直在从事自己专业相关的工作。17年时决定想要转业从事计算机相关领域工作&#xff0c;在17年9月的一个机遇大跨度转行到测试行业&…

vue子组件监听父组件数据变化并作出改变(亲测有效)

vue子组件监听父组件数据变化并作出改变&#xff08;亲测有效&#xff09; 1. 问题 1.1 封装组件时经常会遇到子组件需要根据父组件数据变化并执行对应的操作逻辑 1.2 监听方法中加了deep、immediate 等参数监听数组/对象还是没有生效 1.3 类型table组件需要根据父组件数据…

Java多线程学习——线程的创建、Thread类以及多线程状态

文章目录学习目标一、认识线程1、线程是什么&#xff1f;2、为什么要有线程3、进程和线程的区别二、Thread类以及常见方法1.创建线程的几种方式2、Thread类属性及方法2.1、Thread的常见构造方法2.2、Thread的常见属性3、线程的中断-interrupt()中断一个线程&#xff1a;4、等待…

前端面试题 —— 浏览器原理(一)

目录 一、进程与线程的概念 二、如何实现浏览器内多个标签页之间的通信? 三、浏览器资源缓存的位置有哪些&#xff1f; 四、对浏览器内核的理解 五、常见的浏览器内核比较 六、浏览器的主要组成部分 七、渲染过程中遇到 JS 文件如何处理&#xff1f; 八、什么情况会阻塞…

【C语言】动态内存管理

我们之前开辟的空间&#xff0c;大小固定&#xff0c;且在申明数组的时候&#xff0c;必须指定数组的长度。但是有时候我们需要的空间大小在程序运行的时候才知道&#xff0c;这就得动态内存开辟出马了。 目录 1.malloc和free 2.calloc 3.realloc 4.常见动态内存错误 5.经…

TCP 握手过程 三次 四次

蛋老师视频 SYN 同步 ACK 确认 FIN 结束 核心机制是确定哪些请求或响应需要丢弃 SYN、ACK、FIN 通过 1/0 设置开启/关闭 开启SYN后&#xff0c;报文中会随机生成 Sequence序号 用于校验 &#xff08;应用可能发起多个会话&#xff0c;可以区分&#xff09; 服务器的同步序…

2023版D盾防火墙v2.1.7.2,主动防御保护,以内外保护的方式 防止网站和服务器给入侵。限制了常见的入侵方法,让服务器更安全

v2.1.7.2 (20230107) 2023-1-7 1.修正PHP一处文件检测的bug。 2.修正某些情况下无法文件加白问题。 v2.1.7.2 2022-10-13 1.针对aspx的样本加入了新的识别。 2.针对上传 doc格式文件提示“上传格式不符” 的修正。 3.工具“HTTPS安全”,把 TLS 1.1 和 TLS 1.0 设置为默认不选中…

杰理AD16N简介

一、概述&#xff1a; AD16N是杰理新出的一个MP3解码芯片&#xff0c;是高集成度的 32 位通用音频 SOC&#xff0c; 集成 40KByte SRAM&#xff0c; 时钟源可选内部 RC 或外部12MHz 晶振&#xff0c; 最高主频可达 160MHz&#xff1b; 主要是替代AC109N系列和AC608N、AC104N系列…

Python爬虫书写时遇到的问题汇总

文章目录python的xpath插件需要的库下载出现问题懒加载python 爬取图片,网址都正确但是下不下来的原因:爬取下来的文字包含Windows不能识别的特殊字符selenium的find_element_by_id()出现的问题爬虫信息写入mysql时的1045号错误python的xpath插件需要的库下载出现问题 ERROR: C…

MySQL特殊语法insert into ... on duplicate key update ...

一、前言 在日常开发中&#xff0c;经常会遇到这样的需求&#xff1a;查看某条记录是否存在&#xff0c;不存在的话创建一条新记录&#xff0c;存在的话更新某些字段。 比如下列伪代码&#xff1a; $row mysql_query($result);if($row){mysql_execute(update ...);}else{my…

MongoDB复习

目录 1.docker安装 2.mondo概念解析 3.数据库操作 4.基本数据类型 5. 适合使用场景 6.对集合操作 7.常用操作 1.docker安装 docker pull mongo:latest docker run -d --restartalways -p 27017:27017 --name mymongo -v /data/db:/data/db -d mongo docker exec -it m…

【SpringBoot高级篇】SpringBoot集成Sharding-JDBC分库分表

【SpringBoot高级篇】SpringBoot集成Sharding-JDBC分库分表Apache ShardingSphere分库分表分库分表的方式垂直切分垂直分表垂直分库水平切分水平分库水平分表分库分表带来的问题分库分表中间件Sharding-JDBCsharding-jdbc实现水平分表sharding-jdbc实现水平分库sharding-jdbc实…

数据结构-考研难点代码突破(查找算法 - 散列表(哈希表)C++实现除留余数法拉链法哈希)

文章目录1. 哈希表与解决哈希冲突的方法2. C实现除留余数法拉链法哈希1. 哈希表与解决哈希冲突的方法 散列表(Hash Table)&#xff0c;又称哈希表。是一种数据结构。 特点&#xff1a;数据元素的关键字与其存储地址直接相关。 关键字通过散列函数&#xff08;哈希函数&#…

Vue3.0文档整理:2、创建单页面应用程序

2.1&#xff1a;创建步骤 2.1.1&#xff1a;vue-cli 安装并执行create-vue:npm init vuelatest 它是Vue官方的项目脚手架工具 选择项目功能 除了第一项的项目名字外&#xff0c;其他可以暂时默认回撤或者选择No 切换到项目目录:cd <your-project-name> 安装项目依赖&…

山寨APP频出?安全工程师和黑灰产在较量

在山寨这个领域&#xff0c;没有人比黑灰产更懂模仿。 据安全从业者介绍&#xff0c;一般而言&#xff0c;对于成熟的山寨开发者来说&#xff0c;几天时间内就可以做出一套前端框架。服务器、源代码、域名、服务商这些内容的创建&#xff0c;通过网上租赁的方式就可以解决。 比…

【面试题】2023前端vue面试题及答案

Vue3.0 为什么要用 proxy&#xff1f;在 Vue2 中&#xff0c; 0bject.defineProperty 会改变原始数据&#xff0c;而 Proxy 是创建对象的虚拟表示&#xff0c;并提供 set 、get 和 deleteProperty 等处理器&#xff0c;这些处理器可在访问或修改原始对象上的属性时进行拦截&…

Window问题详解(下)

建议先看一下 Window问题详解(上) 思路② 既然会超时,那该怎么办呢? 显然需要一个更快速的方法来解决这个问题! 我们先来观察一下图片: 我们发现,每一次选中的数都会增加下一个。 !!!!! 因此,我们可以根据此特性优化时间!! 第一次先求出前 k − 1 k-1 k−

hdfs file system shell的简单使用

文章目录1、背景2、hdfs file system shell命令有哪些3、确定shell操作的是哪个文件系统4、本地准备如下文件5、hdfs file system shell5.1 mkdir创建目录5.2 put上传文件5.3 ls查看目录或文件5.4 cat 查看文件内容5.5 head 查看文件前1000字节内容5.6 tail 查看文件后1000字节…