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

本文介绍了如何在项目中集成JWT,包括添加依赖、配置参数、创建JWT配置类和工具类,以及用户注册和登录的实现。在注册过程中,创建了DTO类以统一前后端数据交互,登录时生成并返回token,最后展示了如何验证token的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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源码下载地址:源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

picacho_pkq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值