Springboot项目搭建(1)-用户登录与注册

1.引入lombok依赖

若<dependency>中数据为红,则说明Maven本地仓库里未引用依赖

可在右侧"m"标识中,下载源代码和文档后刷新。

2.统一响应数据Result

在entity文档下创建,名为Result的java类

文件地址:org/example/entity/Result.java

java 复制代码
package org.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//创建统一响应数据的类
@NoArgsConstructor //创建无参数构造器
@AllArgsConstructor //创建有参数构造器
@Data //配置访问属性的方法(get/set)
public class Result<T>{
    private Integer code; //该属性用于存放响应码
    private String message; //提示信息
    private T data; //响应数据,T表示任意类型
    // 创建一个方法,用于返回操作成功的反应结果,
    // 如:查询个人信息,返回操作成功提示及个人信息数据
    public static <E> Result <E> success(E data){
        return new Result<>(0,"操作成功",data);
    }
    // 创建方法,返回操作成功的响应结果,
    // 如:添加数据,删除数据
    public static Result success(){
        return new Result(0,"操作成功",null);
    }
    //创建方法,返回操作失败的响应结果
    public static Result error(String message){
        return new Result(1,message,null);
    }
}

创建一个方法:

public static <E> Result <E> success(E data){
return new Result<>(0,"操作成功",data);

  • public 表示方法的访问权限
  • static 静态只被创建一次,配置该方法存放内存中的方法区
  • <E> 方法的返回类型,E表示任意类型
  • result<E>返回类型,是当前类的类型
  • success方法名称
  • (E data)表示参数列表,调用方法时可以通过参数列表向方法传递数据
  • return 方法返回数据关键字
  • new Result<>() 表示创建对象,创建一个Result对象,
  • 创建对象时传入三个数据,分别是状态码,提示信息以及携带的数据

3. 创建文档

4. 数据格式化User

org/example/entity/User.java路径中,

定义了一个用于存储和传输用户信息的Java实体类。

java 复制代码
package org.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String nickname;
    private String userPic;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

5. 数据库交互UserMapper

声明当前文件为映射文件,与数据库进行数据交互的文件

这个 UserMapper 接口提供了两个基本的数据库操作:添加用户和根据用户名查询用户。

通过MyBatis框架,这些方法可以被Spring框架自动调用,从而实现数据的持久化操作。

这种方式使得数据库操作更加简洁和面向对象。

java 复制代码
package org.example.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.entity.User;
@Mapper
public interface UserMapper {
    //添加用户信息
    @Insert("insert into user(username,password,create_time,update_time)" +
    "values(#{username},#{password},now(),now())")
    void insertUser(String username, String password);
    //根据用户名查询用户信息
    @Select("select * from user where username=#{username}")
    User selectUserByUsername(String username);
}

6. 加密和密码验证

6.1 数据加密 Md5Util

org/example/utils/Md5Util.java中,定义了一个工具类 Md5Util

  • 生成MD5哈希值:将输入的字符串转换为MD5哈希值。
  • 验证密码:检查输入的密码是否与给定的MD5哈希值相同。
  • 将字节数组转换为十六进制字符串:用于表示MD5哈希值的结果。

以下代码来源于网络,有志者也可自行书写 。

java 复制代码
package org.example.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Util {
    /**
     * 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
     */
    protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    protected static MessageDigest messagedigest = null;
    static {
        try {
            messagedigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsaex) {
            System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");
            nsaex.printStackTrace();
        }
    }
    /**
     * 生成字符串的md5校验值
     *
     * @param s
     * @return
     */
    public static String getMD5String(String s) {
        return getMD5String(s.getBytes());
    }

    /**
     * 判断字符串的md5校验码是否与一个已知的md5码相匹配
     *
     * @param password  要校验的字符串
     * @param md5PwdStr 已知的md5校验码
     * @return
     */
    public static boolean checkPassword(String password, String md5PwdStr) {
        String s = getMD5String(password);
        return s.equals(md5PwdStr);
    }
    public static String getMD5String(byte[] bytes) {
        messagedigest.update(bytes);
        return bufferToHex(messagedigest.digest());
    }
    private static String bufferToHex(byte bytes[]) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    private static String bufferToHex(byte bytes[], int m, int n) {
        StringBuffer stringbuffer = new StringBuffer(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            appendHexPair(bytes[l], stringbuffer);
        }
        return stringbuffer.toString();
    }
    private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
        char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>
        // 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
        char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
        stringbuffer.append(c0);
        stringbuffer.append(c1);
    }
}

6.2 密文查看与验证 Md5Test

首先在pom.xml中:导入Spring提供的测试工具

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

</dependency>

而后创建新的测试文件:src/test/java/Md5Test.java

可查看"123456(为例)"转码后的结果。

java 复制代码
import org.example.utils.Md5Util;
import org.junit.jupiter.api.Test;
import java.sql.SQLOutput;
public class Md5Test {
    @Test
    public void test(){
        String md5Password = Md5Util.getMD5String("123456");
        System.out.println(md5Password);
    }
}

7. 定义用户基操规UserService

7.1 创建接口

文件地址:org/example/service/UserService.java

java 复制代码
package org.example.service;
import org.example.entity.User;
public interface UserService {
    //根据用户名查询用户信息
    User findUserByUsername(String username);
    //注册
    void register(String username, String password);
}

7.2 实现接口<lmpl>

文件地址:org/example/service/impl/UserServiceImpl.java

补充UserService.java的具体操作过程

java 复制代码
package org.example.service.impl;
import org.example.entity.User;
import org.example.mapper.UserMapper;
import org.example.service.UserService;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
//声明当前类为业务类,在业务类下可以调用事务相关配置
@Service
public class UserServiceImpl implements UserService {
    //调用UserMapper操作数据库
    @Autowired
    @SuppressWarnings("all") //压制警告
    private UserMapper userMapper;
    @Override
    public User findUserByUsername(String username) {
        return userMapper.selectUserByUsername(username);
    }
    @Override
    public void register(String username, String password) {
        //加密
        String md5String = Md5Util.getMD5String(password);
        userMapper.insertUser(username, md5String);
    }
}

小结:UserServiceImplUserService 接口的具体实现,它提供了接口中声明的方法的实际业务逻辑,并通过依赖注入使用 UserMapper 与数据库进行交互。这种设计模式使得代码结构清晰,易于维护和扩展。

8. 注册UserController

首先在pom.xml中添加:用于请求参数验证(校验)

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-validation</artifactId>

</dependency>

org/example/controller/UserController.java中建立一个UserController 作为表现层,

它的Spring Boot控制器类,负责处理与用户相关的HTTP请求和响应

java 复制代码
package org.example.controller;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController //声明该类是一个控制器类,用于浏览器与该类数据交互
@RequestMapping("/user") //声明该类的访问地址
@Validated //声明该类中的方法支持参数校验
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {
        //查询注册的用户名是否存在
        User user = userService.findUserByUsername(username);
        //判断查询的用户是否存在,如果不存在则返回空
        if (user == null) {
            //该用户名可以使用
            userService.register(username, password);
            //返回操作成功
            return Result.success();
        }else{
            //该用户名不允许使用
            return Result.error("用户名已被占用");
        }
    }

@Pattern(regexp = "^\\S{5,16}$")String username:

  • @Pattern:表示该参数使用校验规则,regexp表示使用正则表达式的校验规则
  • ^:表示正则表达式的开始
  • $:表示正则表达式的结束
  • \\:转译字符,转译后为\
  • \S:表示任意字符
  • {5,16}:字符的位数在5~16间

实验结果:

9. 全局异常类GlobalExceptionHandle

创建org/example/exception/GlobalExceptionHandle.java文件,统一处理异常报告。

java 复制代码
package org.example.exception;
import org.example.entity.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandle {
    //统一返回异常信息
    @ExceptionHandler(value = Exception.class)
    public Result handleException(Exception e) {
        return Result.error(e.getMessage());
    }
}

10. 登录UserController

org/example/controller/UserController.java中的注册代码下方,续写登录代码。

**循环逻辑:**如果上面的if体未执行,则表示该用户存在,由于查询的是用户所有信息,包含数据库中的密码,下面的判断为判断前端传入的密码,与数据库的密码是否相等

java 复制代码
package org.example.controller;
import jakarta.validation.constraints.Pattern;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.utils.JwtUtil;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {...}
    //登录
    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            return Result.success("登录成功");
        }
        return Result.error("登录失败,用户名或密码错误");
    }
}

实验结果:

11. JWT令牌

11.1 认识JWT令牌

全称:JSON Web Token,由Header、Payload、Signature三部分组成:

Header(头):记录令牌类型,参数算法等;

Payload(荷载):携带用户信息等一些自定义的默认信息;

Signature(签名):防止信息被篡改,而设定的算法逻辑密文。

11.2 引入令牌 JwtUtil

添加至org/example/utils/JwtUtil.java

java 复制代码
package org.example.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
    private static final String KEY = "usermessage";
	//接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                .sign(Algorithm.HMAC256(KEY));
    }
	//接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
        return JWT.require(Algorithm.HMAC256(KEY))
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }
}

引入后,在pom.xml中添加:jwt依赖(令牌技术)

<dependency>

<groupId>com.auth0</groupId>

<artifactId>java-jwt</artifactId>

<version>4.4.0</version>

</dependency>

11.3 测试令牌 JwtTest

在test/java下,建立src/test/java/JwtTest.java测试文件:

java 复制代码
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtTest {

    @Test
    public void testJwt() {
        //创建容器(集合),用于存放令牌(用户)数据
        Map<String,Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("name","Hela");
        //生成Jwt
        String token = JWT.create() //创建令牌
                .withClaim("userwagaga",claims) //载荷数据
                .withExpiresAt(new Date(System.currentTimeMillis()*1000*60*60*12)) //过期时间(生命周期)
                .sign(Algorithm.HMAC256("usermessage")); //指定算法,配置加密密钥
        System.out.println(token);
    }
    @Test
    public void testParse(){
        String token ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
                "eyJ1c2Vyd2FnYWdhIjp7Im5hbWUiOiJIZWxhIiwiaWQiOjF9LCJleHAiOjEwMzU0MTc4NDQ2MDY1OTN9." +
                "tbhdeg5kp1u8Hp7NBxT8ALSaAW9-iBLasj_AdIBAYpQ";
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("usermessage")).build();
        DecodedJWT decodedJWT= jwtVerifier.verify(token);
        Map<String, Claim> claimMap = decodedJWT.getClaims();
        System.out.println(claimMap.get("userwagaga"));
    }
}

11.4 将令牌补充至登录代码中

java 复制代码
import java.util.HashMap;
import java.util.Map;

@PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            Map<String,Object> claims = new HashMap<>();// 存放用户令牌数据的容器(集合)
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            claims.put("password",loginUser.getPassword());
            String token = JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("登录失败,用户名或密码错误");
    }

此阶段UserController完整代码:

java 复制代码
package org.example.controller;

import jakarta.validation.constraints.Pattern;
import org.example.entity.Result;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.utils.JwtUtil;
import org.example.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController //声明该类是一个控制器类,用于浏览器与该类数据交互
@RequestMapping("/user") //声明该类的访问地址
@Validated //声明该类中的方法支持参数校验
public class UserController {
    @Autowired
    @SuppressWarnings("all")
    private UserService userService;
    //注册
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {
        //查询注册的用户名是否存在
        User user = userService.findUserByUsername(username);
        //判断查询的用户是否存在,如果不存在则返回空
        if (user == null) {
            //该用户名可以使用
            userService.register(username, password);
            //返回操作成功
            return Result.success();
        }else{
            //该用户名不允许使用
            return Result.error("用户名已被占用");
        }
    }
    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){
        //根据用户名查询用户信息
        User loginUser = userService.findUserByUsername(username);
        //如果没有用户信息则返回登录失败提示
        if(loginUser == null){
            return Result.error("用户名或密码错误");
        }
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            Map<String,Object> claims = new HashMap<>();// 存放用户令牌数据的容器(集合)
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            claims.put("password",loginUser.getPassword());
            String token = JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("登录失败,用户名或密码错误");
    }
}
相关推荐
yuyu_03043 分钟前
SOHE智能厨余垃圾处理系统
java·vue
IT枫斗者5 分钟前
Netty的原理和springboot项目整合
java·spring boot·后端·sql·科技·mysql·spring
Yue丶越5 分钟前
【C语言】动态内存管理
c语言·开发语言
Edward111111117 分钟前
普通java项目转为maven项目 J文件后缀.java变C文件
java·开发语言·maven
赵谨言8 分钟前
基于OpenCV的图像梯度与边缘检测研究
大数据·开发语言·经验分享·python
一雨方知深秋11 分钟前
二.java程序基本语法
java·类型转换·变量·方法·运算符·字面量·关键字标识符
hans汉斯12 分钟前
【软件工程与应用】平移置换搬迁系统设计与实现
数据库·人工智能·系统架构·软件工程·汉斯出版社·软件工程与应用
gugugu.12 分钟前
Redis List类型完全指南:从原理到实战应用
数据库·redis·list
Hello.Reader12 分钟前
Flink SQL ALTER 语句在线演进 Table/View/Function/Catalog/Model
数据库·sql·flink
Java程序之猿16 分钟前
Springboot 集成apache-camel +mqtt 根据主题处理mqtt消息
java·spring boot·后端