srpingboot-后端登录注册功能的实现

目录

一、后端登录功能的实现

1、首先,添加以下依赖

2、创建基本的结构

创建实体类(entity/User.java)

创建基本的DTO

[登录请求DTO - LoginRequest.java](#登录请求DTO - LoginRequest.java)

[登录响应DTO - LoginResponse.java](#登录响应DTO - LoginResponse.java)

[创建登录接口 AuthController.java](#创建登录接口 AuthController.java)

登录服务实现UserService与UserServiceImpl

安全配置(SecurityConfig与JwtUtil)

二、后端注册功能的实现

[1、注册请求 DTO(RegisterRequest)](#1、注册请求 DTO(RegisterRequest))

2、注册接口(AuthController)

3、注册服务实现(UserServiceImpl)

三、测试

1、注册新用户

2、用户登录


需求:登录功能的实现完善

使用技术:

springboot(lombok、Spring Web、MyBatis Framework、MySQL Driver)、Maven、JDK17

后端完整登录功能实现包括:

1、用户实体类

2、登录请求/响应 DTO

3、用户服务接口和实现

4、登录控制器

5、简单的密码加密

6、JWT token 生成和验证

一、后端登录功能的实现

1、首先,添加以下依赖
  1. Spring Security

  2. JWT 支持

  3. Validation 支持(参数校验工具)

    复制代码
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-api</artifactId>
             <version>0.11.5</version>
         </dependency>
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-impl</artifactId>
             <version>0.11.5</version>
             <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-jackson</artifactId>
             <version>0.11.5</version>
             <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.mybatis.spring.boot</groupId>
             <artifactId>mybatis-spring-boot-starter</artifactId>
             <version>3.0.4</version>
         </dependency>
2、创建基本的结构
创建实体类(entity/User.java)
复制代码
package com.userShowing.entity;

import lombok.Data;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;//ID
    private String username;//用户名
    private String password;//密码
    private String email;//邮箱
    private String phone;//手机号
    private LocalDateTime createTime;//注册时间
    private LocalDateTime updateTime;//用户信息更新时间
    private Boolean enabled;//是否启用
}
创建基本的DTO
登录请求DTO - LoginRequest.java
复制代码
package com.userShowing.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class LoginRequest {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    private String password;
}
登录响应DTO - LoginResponse.java
复制代码
package com.userShowing.dto;

import lombok.Data;
import lombok.Builder;

@Data
@Builder
public class LoginResponse {
    private String token;
    private String username;
    private String email;
}
创建登录接口 AuthController.java
复制代码
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private final UserService userService;

    // 使用构造函数进行依赖注入
    public AuthController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@Valid @RequestBody LoginRequest loginRequest) {
        try {
            LoginResponse response = userService.login(loginRequest);
            return ResponseEntity.ok(response);
        } catch (RuntimeException e) {
            Map<String, String> error = new HashMap<>();
            error.put("message", e.getMessage());
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
        } catch (Exception e) {
            Map<String, String> error = new HashMap<>();
            error.put("message", "登录失败,请稍后重试");
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
    }
登录服务实现UserService与UserServiceImpl

UserService

复制代码
package com.useranalysis.service;

import com.useranalysis.dto.LoginRequest;
import com.useranalysis.dto.LoginResponse;
import com.useranalysis.dto.RegisterRequest;
import com.useranalysis.entity.User;

public interface UserService {
    LoginResponse login(LoginRequest loginRequest);//登录
    User findByUsername(String username);//用户查询
    User register(RegisterRequest registerRequest);//注册
} 

UserServiceImpl

复制代码
public LoginResponse login(LoginRequest loginRequest) {
    try {
        // 1. 使用 Spring Security 进行认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );
        
        // 2. 获取认证后的用户详情
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        
        // 3. 生成 JWT token
        String token = jwtUtil.generateToken(userDetails);
        
        // 4. 查询用户详细信息
        User user = userMapper.findByUsername(userDetails.getUsername());
        
        // 5. 构建登录响应
        return LoginResponse.builder()
                .token(token)
                .username(user.getUsername())
                .email(user.getEmail())
                .build();
    } catch (Exception e) {
        // 6. 错误处理
        // 检查用户是否存在
        User user = userMapper.findByUsername(loginRequest.getUsername());
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        // 检查密码格式
        if (!user.getPassword().startsWith("$2a$")) {
            throw new RuntimeException("密码格式不正确,请联系管理员重置密码");
        }
        throw new RuntimeException("用户名或密码错误");
    }
}
安全配置(SecurityConfig与JwtUtil)

SecurityConfig

复制代码
package com.useranalysis.config;

import com.useranalysis.service.impl.CustomUserDetailsService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final CustomUserDetailsService userDetailsService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authenticationProvider(authenticationProvider());
        
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
} 

JwtUtil.java

复制代码
package com.useranalysis.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {
    @Value("${jwt.secret:your-default-secret-key-must-be-at-least-32-chars}")
    private String secret;//存储jwt的签名秘钥

    @Value("${jwt.expiration:86400000}")
    private Long expiration;//存储jwt的过期时间
    //获取签名秘钥
    private Key getSigningKey() {
        byte[] keyBytes = secret.getBytes();
        return Keys.hmacShaKeyFor(keyBytes);
    }
    //从token中提取用户名
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    //从token中提取过期时间
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
    //从token中提取声明
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    //从token中提取所有声明
    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
    //判断token是否过期
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    //生成token
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }
    //生成token
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()//创建一个JWT构建器
                .setClaims(claims)//设置JWT的声明
                .setSubject(subject)//设置JWT的主题
                .setIssuedAt(new Date(System.currentTimeMillis()))//设置JWT的颁发时间
                .setExpiration(new Date(System.currentTimeMillis() + expiration))//设置JWT的过期时间
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)//设置JWT的签名算法
                .compact();//生成JWT字符串
    }
    //验证token
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
} 

3、添加配置文件 (user_analysis更换你自己数据库名称,还有username与password)

复制代码
server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/user_analysis?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

jwt:
  secret: your-secret-key-here-must-be-at-least-32-characters-long
  expiration: 86400000 # 24 hours in milliseconds

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.userShowing.entity

再此之后,还需要:

  1. 确保 MySQL 数据库已经启动,并创建了名为 user_analysis(你自己的也行) 的数据库
  2. 修改 application.yml 中的数据库连接信息(用户名和密码)
  3. 实现 UserServiceImpl 中的 findByUsername 方法,连接到实际的数据库
  • 创建UserMapper接口

    @Mapper
    public interface UserMapper {
    User findByUsername(@Param("username") String username);
    }

  • 创建映射文件UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.useranalysis.mapper.UserMapper"> <select id="findByUsername" resultType="com.useranalysis.entity.User"> SELECT id, username, password, email, phone, create_time, update_time, enabled FROM user WHERE username = #{username} </select> </mapper>
  • 修改 UserServiceImpl 实现类

    import com.usershowing.mapper.UserMapper;

    private final UserMapper userMapper;

    复制代码
      //查询用户
      @Override
      public User findByUsername(String username) {
          return userMapper.findByUsername(username);
      }
  • 数据库表设计

    CREATE TABLE IF NOT EXISTS user (
    id BIGINT NOT NULL AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL,
    phone VARCHAR(20),
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

至此,后端登录功能完成!!!

二、后端注册功能的实现

1、注册请求 DTO(RegisterRequest)
复制代码
package com.useranalysis.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class RegisterRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
    private String password;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
} 
2、注册接口(AuthController)
复制代码
   @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest registerRequest) {
        try {
            User user = userService.register(registerRequest);
            return ResponseEntity.ok(user);
        } catch (RuntimeException e) {
            Map<String, String> error = new HashMap<>();
            error.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(error);
        } catch (Exception e) {
            Map<String, String> error = new HashMap<>();
            error.put("message", "注册失败,请稍后重试");
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
    }
}
3、注册服务实现(UserServiceImpl)
复制代码
@Transactional
public User register(RegisterRequest registerRequest) {
    // 1. 检查用户名是否已存在
    if (findByUsername(registerRequest.getUsername()) != null) {
        throw new RuntimeException("用户名已存在");
    }

    // 2. 创建新用户
    User user = new User();
    user.setUsername(registerRequest.getUsername());
    // 3. 使用 BCrypt 加密密码
    user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
    user.setEmail(registerRequest.getEmail());
    user.setEnabled(true);
    user.setCreateTime(LocalDateTime.now());
    user.setUpdateTime(LocalDateTime.now());

    // 4. 保存用户到数据库
    userMapper.insert(user);
    return user;
}

UserMapper

复制代码
void insert(User user);

UserMapper.xml映射语句

复制代码
    <insert id="insert" parameterType="com.useranalysis.entity.User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user (username, password, email, phone, create_time, update_time, enabled)
        VALUES (#{username}, #{password}, #{email}, #{phone}, #{createTime}, #{updateTime}, #{enabled})
    </insert>

三、测试

启动项目,使用postman/apifox 测试接口:

1、注册新用户
复制代码
POST /api/auth/register
Content-Type: application/json

{
    "username": "testuser",
    "password": "password123",
    "email": "test@example.com"
}

测试返回数据:

2、用户登录
复制代码
POST /api/auth/login
Content-Type: application/json

{
    "username": "testuser",
    "password": "password123"
}

测试返回数据:

项目完整文件地址:

通过百度网盘分享的文件:user-analysis

链接: https://pan.baidu.com/s/1Oi_EMBIN9dEdtHCPaGmCbQ?pwd=jbwd 提取码: jbwd

相关推荐
Alsn867 分钟前
27.IDEA 专业版创建与打包 Java 命令行程序
java·ide·intellij-idea
啦啦啦~~~7548 分钟前
【最新版】Edge浏览器安装!绿色增强版+禁止Edge更新的软件+彻底卸载Edge软件
数据库·阿里云·电脑·.net·edge浏览器
毕设源码-郭学长10 分钟前
【开题答辩全过程】以 基于JAVA的车辆违章信息管理系统设计及实现为例,包含答辩的问题和答案
java·开发语言
while(1){yan}11 分钟前
UDP和TCP的核心
java·开发语言·网络·网络协议·tcp/ip·udp
程序边界12 分钟前
金仓数据库助力Oracle迁移:一场国产数据库的逆袭之旅
数据库·oracle
为什么不问问神奇的海螺呢丶14 分钟前
oracle RAC开关机步骤
数据库·oracle
麒qiqi14 分钟前
【Linux 进程间通信】信号通信与共享内存核心解析
java·linux·算法
后端小张16 分钟前
【Java 进阶】深入理解Redis:从基础应用到进阶实践全解析
java·开发语言·数据库·spring boot·redis·spring·缓存
柯南二号16 分钟前
【后端】【Java】RabbitMQ / RocketMQ / Kafka / Redis 消息队列深度对比与选型指南
java·java-rocketmq·java-rabbitmq
TDengine (老段)18 分钟前
TDengine IDMP 1.0.9.0 上线:数据建模、分析运行与可视化能力更新一览
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据