Spring Boot + Spring Security + JWT 登录认证完整实现

一、整体流程梳理 🚀

这是一套 Spring Boot + Spring Security + JWT 的登录认证代码,核心流程是:

前端发送登录请求 → Controller 接收参数 → Service 层完成身份认证 → 生成 JWT 令牌返回给前端 → 前端后续请求携带 Token → 后端验证 Token 有效性。


二、Controller 层代码讲解(LoginController.java)

这是请求入口,负责接收前端的登录请求并调用业务逻辑。

java 复制代码
package com.kob.backend.controller.user.account;

import com.kob.backend.service.user.account.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping("/user/account/token/")
    public Map<String, String> getToken(@RequestParam Map<String, String> map) {
        String username = map.get("username");
        String password = map.get("password");
        return loginService.getToken(username, password);
    }
}
关键注解与代码解析:
  1. @RestController

    • 标记这是一个控制器类 ,所有方法的返回值会直接作为 HTTP 响应体(相当于 @Controller + @ResponseBody)。
    • 告诉 Spring:这个类要处理前端请求。
  2. @Autowired

    • 依赖注入:Spring 会自动找到 LoginService 的实现类(LoginServiceImpl)并注入进来,不用手动 new 对象。
    • 作用:解耦,让 Controller 直接调用 Service 的业务方法。
  3. @PostMapping("/user/account/token/")

    • 处理 POST 请求 ,请求路径是 /user/account/token/
    • 前端需要用 POST 方法访问这个地址,携带 usernamepassword 参数。
  4. getToken(@RequestParam Map<String, String> map)

    • @RequestParam Map<String, String> map:把前端传来的请求参数(比如表单/URL 参数)封装成一个 Map,key 是参数名,value 是参数值。
    • 从 Map 中取出 usernamepassword,传给 Service 层的 getToken 方法处理。
    • 最终返回 Service 层的结果(包含 JWT 令牌的 Map)给前端。

三、Service 层代码讲解

3.1 LoginService 接口(LoginService.java)

接口定义业务方法,解耦接口与实现,符合面向接口编程思想。

java 复制代码
package com.kob.backend.service.user.account;

import java.util.Map;

public interface LoginService {
    /**
     * 处理登录逻辑,生成JWT令牌
     * @param username 用户名
     * @param password 密码
     * @return 包含令牌和状态的Map
     */
    Map<String, String> getToken(String username, String password);
}
3.2 LoginServiceImpl 实现类(LoginServiceImpl.java)

这是业务逻辑层,负责完成身份认证和生成 JWT 令牌。

java 复制代码
package com.kob.backend.service.user.account;

import com.kob.backend.pojo.User;
import com.kob.backend.service.utils.UserDetailsImpl;
import com.kob.backend.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

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

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public Map<String, String> getToken(String username, String password) {
        // 1. 封装用户名密码为 Spring Security 认证令牌
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);

        // 2. 调用认证管理器完成认证(失败会自动抛异常)
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        // 3. 从认证结果中获取用户信息
        UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
        User user = loginUser.getUser();

        // 4. 生成 JWT 令牌
        String jwt = JwtUtil.createJWT(user.getId().toString());

        // 5. 封装结果返回
        Map<String, String> map = new HashMap<>();
        map.put("error_message", "success");
        map.put("token", jwt);

        return map;
    }
}
关键注解与代码解析:
  1. @Service

    • 标记这是一个业务层组件,Spring 会将其纳入容器管理,方便 Controller 注入。
    • 作用:处理核心业务逻辑,和数据库/安全框架交互。
  2. @Autowired AuthenticationManager

    • 注入 Spring Security 的认证管理器,是处理用户名密码认证的核心组件。
  3. UsernamePasswordAuthenticationToken

    • 把前端传来的 usernamepassword 封装成 Spring Security 能识别的认证令牌
    • 相当于把用户的登录凭证打包,交给认证管理器去验证。
  4. authenticationManager.authenticate(authenticationToken)

    • 执行认证:Spring Security 会调用你配置的 UserDetailsService 去数据库查用户信息,对比密码是否正确。
    • ✅ 认证成功:返回包含用户信息的 Authentication 对象。
    • ❌ 认证失败(用户名不存在/密码错误):自动抛出异常,Spring 会全局处理并返回错误响应,代码里不需要手动处理失败情况。
  5. authenticate.getPrincipal()

    • 从认证成功的结果中获取当前登录用户 ,强转为你自定义的 UserDetailsImpl(实现了 Spring Security 的 UserDetails 接口,用来封装用户信息)。
    • 再从 UserDetailsImpl 中拿到你自己的 User 实体类,获取用户 ID。
  6. JwtUtil.createJWT(user.getId().toString())

    • 调用工具类 JwtUtil,传入用户 ID,生成一个 JWT 令牌
    • JWT 是前后端分离认证的核心:前端拿到后,后续请求会在请求头里带上这个 Token,服务端验证 Token 有效性来确认用户身份,不需要存储 Session。
  7. 封装返回结果

    • HashMap 封装结果:
      • error_message: "success":告诉前端认证成功。
      • token: jwt:返回生成的 JWT 令牌。
    • 这个 Map 最终会被 Controller 返回给前端。

四、核心依赖(pom.xml)

实现该功能需要引入以下核心依赖,基于 Spring Boot 2.x/3.x 适配:

xml 复制代码
<!-- Spring Boot Web 核心 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Security 安全框架 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JWT 核心依赖 (jjwt) -->
<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>

<!-- MyBatis-Plus (操作数据库) -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- Lombok (简化实体类) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

五、核心工具类/实体类补充

5.1 JWT 工具类(JwtUtil.java)

负责 JWT 令牌的生成、验证,是认证的核心工具类:

java 复制代码
package com.kob.backend.utils;

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.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;

@Component
public class JwtUtil {
    // Token 过期时间:2小时 (毫秒)
    private static final long EXPIRATION_TIME = 7200000;
    
    // 密钥(建议配置在 application.yml 中,此处简化)
    @Value("${jwt.secret}")
    private String secret;

    // 生成 JWT Token
    public String createJWT(String userId) {
        // 生成密钥
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
        
        // 构建 Token
        return Jwts.builder()
                .setSubject(userId) // 存储用户ID
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间
                .signWith(key, SignatureAlgorithm.HS256) // 签名算法
                .compact();
    }

    // 验证 Token 并解析用户ID
    public String parseJWT(String token) {
        SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
        
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
        
        // 返回存储的用户ID
        return claims.getSubject();
    }
}
5.2 User 实体类(User.java)

对应数据库用户表,封装用户基础信息:

java 复制代码
package com.kob.backend.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user") // 对应数据库表名
public class User {
    @TableId(type = IdType.AUTO) // 自增主键
    private Integer id;          // 用户ID
    private String username;     // 用户名
    private String password;     // 加密后的密码
    private String nickname;     // 昵称(可选)
}
5.3 UserDetailsImpl(UserDetailsImpl.java)

实现 Spring Security 的 UserDetails 接口,封装用户信息供 Security 使用:

java 复制代码
package com.kob.backend.service.utils;

import com.kob.backend.pojo.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;

// 封装用户信息,适配 Spring Security
public class UserDetailsImpl implements UserDetails {
    private final User user;

    public UserDetailsImpl(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    // 权限控制(此处简化,返回空集合)
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.emptyList();
    }

    // 获取密码(Security 会用这个密码和前端传入的对比)
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    // 获取用户名
    @Override
    public String getUsername() {
        return user.getUsername();
    }

    // 账号是否未过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    // 账号是否未锁定
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    // 凭证是否未过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    // 账号是否可用
    @Override
    public boolean isEnabled() {
        return true;
    }
}
5.4 UserDetailsService 实现类(UserDetailsServiceImpl.java)

Security 核心类,负责从数据库查询用户信息:

java 复制代码
package com.kob.backend.service.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    // Security 认证时自动调用,根据用户名查用户
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 从数据库查询用户
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);
        
        // 2. 用户不存在则抛异常
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        
        // 3. 封装成 UserDetails 返回
        return new UserDetailsImpl(user);
    }
}

六、Spring Security 核心配置(SecurityConfig.java)

配置认证管理器、密码加密、放行接口等核心规则:

java 复制代码
package com.kob.backend.config;

import com.kob.backend.service.utils.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity // 启用 Spring Security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    // 密码加密器(必须配置,否则 Security 报错)
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 配置认证管理器(供 Service 层调用)
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 核心配置:放行接口、关闭Session、配置认证逻辑
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 关闭跨域防护(前后端分离必备)
            .cors().and().csrf().disable()
            // 关闭 Session(JWT 是无状态认证)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // 配置接口权限
            .authorizeRequests()
                // 登录接口放行,无需认证
                .antMatchers("/user/account/token/").permitAll()
                // 其他所有接口需要认证
                .anyRequest().authenticated();
    }

    // 配置用户认证逻辑:指定用户信息来源和密码加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

七、全局异常处理(GlobalExceptionHandler.java)

统一处理认证失败等异常,返回友好提示给前端:

java 复制代码
package com.kob.backend.config;

import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

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

// 全局异常处理器,处理 Controller 层异常
@RestControllerAdvice
public class GlobalExceptionHandler {
    // 处理用户名不存在异常
    @ExceptionHandler(UsernameNotFoundException.class)
    public Map<String, String> handleUsernameNotFound(UsernameNotFoundException e) {
        Map<String, String> map = new HashMap<>();
        map.put("error_message", "用户名不存在");
        return map;
    }

    // 处理密码错误异常
    @ExceptionHandler(BadCredentialsException.class)
    public Map<String, String> handleBadCredentials(BadCredentialsException e) {
        Map<String, String> map = new HashMap<>();
        map.put("error_message", "密码错误");
        return map;
    }

    // 处理其他异常
    @ExceptionHandler(Exception.class)
    public Map<String, String> handleException(Exception e) {
        Map<String, String> map = new HashMap<>();
        map.put("error_message", "服务器内部错误");
        return map;
    }
}

八、application.yml 配置

核心配置文件,配置数据库、JWT 密钥等:

yaml 复制代码
spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/你的数据库名?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root # 你的数据库用户名
    password: 123456 # 你的数据库密码
  # 关闭 Spring Security 控制台密码
  security:
    user:
      name: admin
      password: admin

# JWT 配置
jwt:
  secret: your-secret-key-1234567890abcdefghijklmnopqrstuvwxyz # 自定义密钥(建议至少32位)

# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 下划线转驼峰
  mapper-locations: classpath:mapper/*.xml # Mapper.xml 路径
  type-aliases-package: com.kob.backend.pojo # 实体类包路径

九、完整请求流程

  1. 前端发送 POST 请求到 /user/account/token/,携带 usernamepassword
  2. LoginController 接收参数,调用 LoginService.getToken()
  3. LoginService 封装用户名密码为 UsernamePasswordAuthenticationToken,交给 AuthenticationManager 认证。
  4. AuthenticationManager 调用 UserDetailsServiceImpl.loadUserByUsername() 从数据库查询用户。
  5. Security 自动对比数据库密码(BCrypt 加密)与前端传入密码,验证通过则返回 Authentication 对象。
  6. Service 层从 Authentication 中获取用户 ID,调用 JwtUtil 生成 Token。
  7. 封装 success + Token 返回给 Controller,最终返回给前端。
  8. 前端存储 Token,后续请求在 Authorization 请求头中携带 Bearer + Token,后端验证 Token 有效性。

十、核心技术点总结 💡

技术/注解 作用
@RestController 标记控制器,返回值直接作为 HTTP 响应体
@Service 标记业务层组件,由 Spring 管理
@Autowired 依赖注入,自动装配 Bean
@PostMapping 处理 POST 请求,指定请求路径
@RestControllerAdvice 全局异常处理,统一返回格式
AuthenticationManager Spring Security 核心认证管理器
UserDetailsService 自定义用户信息查询逻辑,对接数据库
BCryptPasswordEncoder 密码加密/验证,保障密码安全
JWT 生成无状态的身份令牌,实现前后端分离认证

十一、注意事项 ⚠️

  1. 密码必须加密存储:数据库中不能存明文密码,注册/修改密码时需用 BCryptPasswordEncoder 加密。
  2. JWT 密钥要足够复杂:建议至少 32 位随机字符串,避免被破解。
  3. Token 过期时间合理:过短影响用户体验,过长增加安全风险(建议 1-2 小时)。
  4. 敏感接口必须认证:除登录/注册外,其他接口需验证 Token 有效性。
  5. 异常处理要友好:避免直接返回堆栈信息给前端,统一返回 error_message

总结

  1. 这套登录认证体系核心是Spring Security 完成身份验证 + JWT 生成无状态令牌,实现前后端分离下的安全认证。
  2. 核心流程分为:请求接收 → 身份认证 → 生成 Token → 返回结果,关键是 AuthenticationManager 认证和 JWT 令牌生成。
  3. 必须配置 UserDetailsService 对接数据库、PasswordEncoder 加密密码、全局异常处理,才能保证功能完整且安全。
相关推荐
loading小马2 小时前
解决idea2024版本Services栏没有显示Springboot窗口问题
java·spring boot·后端
好学且牛逼的马2 小时前
Spring Boot 3 核心实战指南
java·spring boot·后端
147API2 小时前
Claude API 429 限速治理:RPM/ITPM/OTPM + 令牌桶(Kotlin)
java·spring·kotlin·claude
身如柳絮随风扬2 小时前
Apache POI导出Word,PPT完整实现
spring boot·word·powerpoint·apache
IT枫斗者2 小时前
CentOS 7 一键部署 K8s 1.23 + Rancher 2.7 完整指南
java·linux·spring boot·后端·kubernetes·centos·rancher
qq_12498707532 小时前
基于springboot的个性化服装搭配推荐小程序(源码+论文+部署+安装)
spring boot·后端·spring·微信小程序·小程序·毕业设计·毕业设计源码
拾贰_C2 小时前
【idea | knife4j | springboot2/3|接上篇】knife4j版本号与spring boot版本不兼容问题
java·spring boot·intellij-idea
222you2 小时前
MongoDB的安装和整合SpringBoot
数据库·spring boot·mongodb
云烟成雨TD2 小时前
Spring AI 1.x 系列【9】ChatOptions 配置解析
java·人工智能·spring