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

发布于:2025-03-26 ⋅ 阅读:(35) ⋅ 点赞:(0)

目录

一、后端登录功能的实现

1、首先,添加以下依赖

2、创建基本的结构

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

创建基本的DTO

登录请求DTO - LoginRequest.java

登录响应DTO - LoginResponse.java

创建登录接口 AuthController.java

 登录服务实现UserService与UserServiceImpl

 安全配置(SecurityConfig与JwtUtil) 

二、后端注册功能的实现

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 
         


        网站公告

        今日签到

        点亮在社区的每一天
        去签到