作者 | Java匠艺

日期 | 2026年农历丙午年正月廿一

系列 | Spring Boot 3.2.5 + Spring Cloud + Spring Cloud Alibaba 微服务实战


🎯 本章目标

在微服务架构中,认证中心是安全防护的第一道防线。今天,我将手把手教你搭建一个功能完整、安全可靠的认证中心。无论你是架构师还是开发工程师,这篇文章都将为你提供从零到一的完整解决方案。


📁 认证中心架构设计

架构图┌─────────────────────────────────────────────────────┐│                   认证中心架构图                       │├─────────────────────────────────────────────────────┤│  客户端 → 网关 → 认证中心 → 业务服务 → 数据库/Redis       │└─────────────────────────────────────────────────────┘     │         │         │         │           │     │         │         ├─────────┼───────────┤     │         │         │ 用户管理 │ 角色管理  │     │         │         │ 权限管理 │ Token管理 │     │         │         │ 验证码  │ 登录记录  │     │         │         └─────────┴───────────┘     │         │     │         └── 统一认证入口     │     └── 多端接入

核心功能模块

  1. 用户认证:登录、注册、注销

  2. 权限管理:角色、权限分配

  3. Token管理:JWT生成、验证、刷新

  4. 安全防护:验证码、登录限制

  5. 会话管理:分布式会话


🚀 第一步:项目环境准备

1.1 创建认证中心模块

在父项目的pom.xml中已经定义好了microservice-auth模块,现在我们创建完整的项目结构:

# 创建认证中心目录结构mkdir -p microservice-auth/src/{main,test}/java/com/tech/authmkdir -p microservice-auth/src/main/resources/{mapper,sql}

1.2 数据库设计

创建auth_service.sql

-- 创建数据库CREATE DATABASE IF NOT EXISTS `tech_auth` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE `tech_auth`;-- 用户表CREATE TABLE `sys_user` (  `id` bigint NOT NULL COMMENT '主键ID',  `username` varchar(50) NOT NULL COMMENT '用户名',  `password` varchar(255) NOT NULL COMMENT '密码',  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',  `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',  `avatar` varchar(500) DEFAULT NULL COMMENT '头像',  `gender` tinyint DEFAULT '0' COMMENT '性别:0-未知 1-男 2-女',  `birthday` datetime DEFAULT NULL COMMENT '生日',  `last_login_ip` varchar(50) DEFAULT NULL COMMENT '最后登录IP',  `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',  `status` tinyint DEFAULT '1' COMMENT '状态:1-启用 0-禁用 2-锁定',  `dept_id` bigint DEFAULT NULL COMMENT '部门ID',  `create_by` varchar(50) DEFAULT NULL COMMENT '创建人',  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `update_by` varchar(50) DEFAULT NULL COMMENT '更新人',  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  `is_deleted` tinyint DEFAULT '0' COMMENT '删除标志:0-未删除 1-已删除',  `remark` varchar(500) DEFAULT NULL COMMENT '备注',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_username` (`username`),  UNIQUE KEY `uk_phone` (`phone`),  UNIQUE KEY `uk_email` (`email`),  KEY `idx_dept_id` (`dept_id`),  KEY `idx_status` (`status`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';-- 角色表CREATE TABLE `sys_role` (  `id` bigint NOT NULL COMMENT '主键ID',  `role_code` varchar(50) NOT NULL COMMENT '角色编码',  `role_name` varchar(50) NOT NULL COMMENT '角色名称',  `description` varchar(200) DEFAULT NULL COMMENT '角色描述',  `sort` int DEFAULT '0' COMMENT '排序',  `enabled` tinyint DEFAULT '1' COMMENT '是否启用:0-禁用 1-启用',  `create_by` varchar(50) DEFAULT NULL COMMENT '创建人',  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `update_by` varchar(50) DEFAULT NULL COMMENT '更新人',  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  `is_deleted` tinyint DEFAULT '0' COMMENT '删除标志:0-未删除 1-已删除',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_role_code` (`role_code`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';-- 权限表CREATE TABLE `sys_permission` (  `id` bigint NOT NULL COMMENT '主键ID',  `parent_id` bigint DEFAULT '0' COMMENT '父权限ID',  `permission_code` varchar(100) NOT NULL COMMENT '权限编码',  `permission_name` varchar(50) NOT NULL COMMENT '权限名称',  `type` varchar(20) NOT NULL COMMENT '权限类型:MENU-菜单 BUTTON-按钮 API-接口',  `path` varchar(200) DEFAULT NULL COMMENT '权限路径',  `component` varchar(200) DEFAULT NULL COMMENT '组件路径',  `icon` varchar(100) DEFAULT NULL COMMENT '图标',  `sort` int DEFAULT '0' COMMENT '排序',  `enabled` tinyint DEFAULT '1' COMMENT '是否启用:0-禁用 1-启用',  `hidden` tinyint DEFAULT '0' COMMENT '是否隐藏:0-显示 1-隐藏',  `create_by` varchar(50) DEFAULT NULL COMMENT '创建人',  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',  `update_by` varchar(50) DEFAULT NULL COMMENT '更新人',  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',  `is_deleted` tinyint DEFAULT '0' COMMENT '删除标志:0-未删除 1-已删除',  `remark` varchar(500) DEFAULT NULL COMMENT '备注',  PRIMARY KEY (`id`),  UNIQUE KEY `uk_permission_code` (`permission_code`),  KEY `idx_parent_id` (`parent_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='权限表';-- 用户角色关联表CREATE TABLE `sys_user_role` (  `user_id` bigint NOT NULL COMMENT '用户ID',  `role_id` bigint NOT NULL COMMENT '角色ID',  PRIMARY KEY (`user_id`,`role_id`),  KEY `idx_role_id` (`role_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色关联表';-- 角色权限关联表CREATE TABLE `sys_role_permission` (  `role_id` bigint NOT NULL COMMENT '角色ID',  `permission_id` bigint NOT NULL COMMENT '权限ID',  PRIMARY KEY (`role_id`,`permission_id`),  KEY `idx_permission_id` (`permission_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色权限关联表';-- 初始化数据INSERT INTO `sys_user` (`id`, `username`, `password`, `nickname`, `real_name`, `phone`, `email`, `status`, `remark`) VALUES(1, 'admin', '$2a$10$E9J4Y5jM4k7jV8J5V6mZ8u5V7M6J5K4L3J2H1G0F9E8D7C6B5A4V3C2B1A0', '管理员', '系统管理员', '13800138000', 'admin@tech.com', 1, '超级管理员');INSERT INTO `sys_role` (`id`, `role_code`, `role_name`, `description`, `sort`) VALUES(1, 'ROLE_ADMIN', '管理员', '系统管理员,拥有所有权限', 1),(2, 'ROLE_USER', '普通用户', '普通用户,拥有基本权限', 2);INSERT INTO `sys_permission` (`id`, `parent_id`, `permission_code`, `permission_name`, `type`, `path`, `component`, `sort`) VALUES(1, 0, 'system:manage', '系统管理', 'MENU', '/system', 'Layout', 1),(2, 1, 'system:user:list', '用户管理', 'MENU', '/system/user', 'system/user/index', 1),(3, 2, 'system:user:query', '用户查询', 'API', NULL, NULL, 1),(4, 2, 'system:user:add', '用户新增', 'API', NULL, NULL, 2),(5, 2, 'system:user:edit', '用户编辑', 'API', NULL, NULL, 3),(6, 2, 'system:user:delete', '用户删除', 'API', NULL, NULL, 4);-- 分配用户角色INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (1, 1);-- 分配角色权限INSERT INTO `sys_role_permission` (`role_id`, `permission_id`) VALUES(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6),(2, 3);


📦 第二步:核心代码实现

pom文件

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0          http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.tech</groupId>        <artifactId>microservice-parent</artifactId>        <version>1.0.0</version>    </parent>
    <artifactId>microservice-auth</artifactId>    <packaging>jar</packaging>    <name>microservice-auth</name>    <description>微服务认证中心(精简版)</description>
    <dependencies>        <!-- ========== Spring基础依赖 ========== -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-validation</artifactId>        </dependency>
        <!-- ========== Spring Security ========== -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-security</artifactId>        </dependency>
        <!-- ========== 数据库相关 ========== -->        <dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus-boot-starter</artifactId>        </dependency>        <dependency>            <groupId>com.mysql</groupId>            <artifactId>mysql-connector-j</artifactId>        </dependency>
        <!-- ========== Redis缓存 ========== -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis</artifactId>        </dependency>
        <!-- ========== 公共模块 ========== -->        <dependency>            <groupId>com.tech</groupId>            <artifactId>microservice-common</artifactId>            <version>1.0.0</version>        </dependency>
        <!-- ========== JWT认证 ========== -->        <dependency>            <groupId>io.jsonwebtoken</groupId>            <artifactId>jjwt-api</artifactId>        </dependency>        <dependency>            <groupId>io.jsonwebtoken</groupId>            <artifactId>jjwt-impl</artifactId>            <scope>runtime</scope>        </dependency>        <dependency>            <groupId>io.jsonwebtoken</groupId>            <artifactId>jjwt-jackson</artifactId>            <scope>runtime</scope>        </dependency>
        <!-- ========== 验证码 ========== -->        <dependency>            <groupId>com.github.whvcse</groupId>            <artifactId>easy-captcha</artifactId>            <version>1.6.2</version>        </dependency>
        <!-- ========== 工具类 ========== -->        <dependency>            <groupId>cn.hutool</groupId>            <artifactId>hutool-all</artifactId>        </dependency>    </dependencies>
    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

bootstrap.yml

spring:  application:    name: microservice-auth  profiles:    active: dev  cloud:    nacos:      config:        server-addr: localhost:8848        file-extension: yaml      discovery:        server-addr: localhost:8848

application.yml

# 服务配置server:  port: 8081  servlet:    context-path: /auth-service
# 数据库配置spring:  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/tech_auth?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai    username: root    password: 123456    hikari:      maximum-pool-size: 20
  # Redis配置  redis:    host: localhost    port: 6379    database: 0
  # 安全配置  security:    user:      name: admin      password: admin123
# JWT配置jwt:  secret: tech2026authsecretkey1234567890  expiration: 7200  refresh-expiration: 2592000  header: Authorization  prefix: Bearer
# 验证码配置captcha:  width: 130  height: 48  length: 4  expire-seconds: 300
# MyBatis Plus配置mybatis-plus:  mapper-locations: classpath*:/mapper/**/*.xml  type-aliases-package: com.tech.auth.domain.entity  configuration:    map-underscore-to-camel-case: true  global-config:    db-config:      id-type: ASSIGN_ID

AuthApplication.java

package com.tech.auth;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/** * 认证中心启动类 */@SpringBootApplication@EnableDiscoveryClientpublic class AuthApplication {
    public static void main(String[] args) {        SpringApplication.run(AuthApplication.class, args);    }}

2.1 实体类实现

User.java

package com.tech.auth.domain.entity;import com.baomidou.mybatisplus.annotation.*;import com.tech.auth.enums.GenderEnum;import com.tech.auth.enums.UserStatusEnum;import com.tech.common.domain.entity.BaseEntity;import lombok.Data;import lombok.EqualsAndHashCode;import java.time.LocalDateTime;@Data@EqualsAndHashCode(callSuper = true)@TableName("sys_user")public class User extends BaseEntity {    @TableField("username")    private String username;    @TableField("password")    private String password;    @TableField("nickname")    private String nickname;    @TableField("real_name")    private String realName;    @TableField("phone")    private String phone;    @TableField("email")    private String email;    @TableField("avatar")    private String avatar;    @TableField("gender")    private GenderEnum gender = GenderEnum.UNKNOWN;    @TableField("birthday")    private LocalDateTime birthday;    @TableField("last_login_ip")    private String lastLoginIp;    @TableField("last_login_time")    private LocalDateTime lastLoginTime;    @TableField("status")    private UserStatusEnum status = UserStatusEnum.ENABLED;    @TableField("dept_id")    private Long deptId;    @TableField("remark")    private String remark;}

2.2 安全配置

SecurityConfig.java(核心安全配置)

package com.tech.auth.config;import com.tech.auth.security.filter.JwtAuthenticationFilter;import com.tech.auth.security.handler.*;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.dao.DaoAuthenticationProvider;import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;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;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.web.cors.CorsConfiguration;import org.springframework.web.cors.CorsConfigurationSource;import org.springframework.web.cors.UrlBasedCorsConfigurationSource;import java.util.Arrays;@Configuration@EnableWebSecurity@EnableMethodSecurity(prePostEnabled = true)@RequiredArgsConstructorpublic class SecurityConfig {    private final UserDetailsService userDetailsService;    private final JwtAuthenticationFilter jwtAuthenticationFilter;    private final AccessDeniedHandlerImpl accessDeniedHandler;    private final AuthenticationEntryPointImpl authenticationEntryPoint;    /**     * 密码加密器     */    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }    /**     * 认证提供者     */    @Bean    public DaoAuthenticationProvider authenticationProvider() {        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();        provider.setUserDetailsService(userDetailsService);        provider.setPasswordEncoder(passwordEncoder());        provider.setHideUserNotFoundExceptions(false);        return provider;    }    /**     * 认证管理器     */    @Bean    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {        return config.getAuthenticationManager();    }    /**     * 安全过滤器链     */    @Bean    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {        http            // 禁用CSRF和Session            .csrf().disable()            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)            .and()            // 认证配置            .authenticationProvider(authenticationProvider())            // 异常处理            .exceptionHandling()                .authenticationEntryPoint(authenticationEntryPoint)                .accessDeniedHandler(accessDeniedHandler)            .and()            // 授权配置            .authorizeHttpRequests(authorize -> authorize                // 公开接口                .antMatchers(                    "/auth/login",                    "/auth/register",                    "/auth/captcha/**",                    "/auth/refresh-token",                    // Swagger文档                    "/doc/**",                    "/webjars/**",                    "/v3/api-docs/**",                    "/swagger-ui/**",                    "/swagger-resources/**",                    // 健康检查                    "/actuator/**"                ).permitAll()                // 其他所有接口需要认证                .anyRequest().authenticated()            )            // 添加JWT过滤器            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)            // 跨域配置            .cors().configurationSource(corsConfigurationSource());        return http.build();    }    /**     * 跨域配置     */    @Bean    public CorsConfigurationSource corsConfigurationSource() {        CorsConfiguration configuration = new CorsConfiguration();        configuration.setAllowedOriginPatterns(Arrays.asList("*"));        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));        configuration.setAllowedHeaders(Arrays.asList("*"));        configuration.setAllowCredentials(true);        configuration.setMaxAge(3600L);        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();        source.registerCorsConfiguration("/**", configuration);        return source;    }}

2.3 JWT工具类

JwtTokenProvider.java

package com.tech.auth.security.component;import com.tech.auth.security.constant.SecurityConstant;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import io.jsonwebtoken.security.Keys;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.stereotype.Component;import javax.crypto.SecretKey;import java.nio.charset.StandardCharsets;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.function.Function;import java.util.stream.Collectors;@Slf4j@Componentpublic class JwtTokenProvider {    @Value("${jwt.secret}")    private String secret;    @Value("${jwt.expiration}")    private Long expiration;    @Value("${jwt.refresh-expiration}")    private Long refreshExpiration;    /**     * 生成Access Token     */    public String generateAccessToken(UserDetails userDetails) {        Map<String, Object> claims = new HashMap<>();        claims.put(SecurityConstant.CLAIM_KEY_USERNAME, userDetails.getUsername());        claims.put(SecurityConstant.CLAIM_KEY_AUTHORITIES,                 userDetails.getAuthorities().stream()                        .map(GrantedAuthority::getAuthority)                        .collect(Collectors.toList()));        claims.put(SecurityConstant.CLAIM_KEY_CREATED, new Date());        return generateToken(claims, expiration);    }    /**     * 生成Access Token     */    public String generateAccessToken(String username, Long userId, String... authorities) {        Map<String, Object> claims = new HashMap<>();        claims.put(SecurityConstant.CLAIM_KEY_USER_ID, userId);        claims.put(SecurityConstant.CLAIM_KEY_USERNAME, username);        if (authorities != null && authorities.length > 0) {            claims.put(SecurityConstant.CLAIM_KEY_AUTHORITIES, authorities);        }        claims.put(SecurityConstant.CLAIM_KEY_CREATED, new Date());        return generateToken(claims, expiration);    }    /**     * 生成Refresh Token     */    public String generateRefreshToken(String username) {        Map<String, Object> claims = new HashMap<>();        claims.put(SecurityConstant.CLAIM_KEY_USERNAME, username);        claims.put(SecurityConstant.CLAIM_KEY_CREATED, new Date());        return generateToken(claims, refreshExpiration);    }    /**     * 生成Token     */    private String generateToken(Map<String, Object> claims, Long expiration) {        return Jwts.builder()                .setClaims(claims)                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))                .signWith(getSecretKey(), SignatureAlgorithm.HS512)                .compact();    }    /**     * 验证Token     */    public boolean validateToken(String token) {        try {            Claims claims = getClaimsFromToken(token);            Date expiration = claims.getExpiration();            return !expiration.before(new Date());        } catch (Exception e) {            log.error("Token验证失败: {}", e.getMessage());            return false;        }    }    /**     * 从Token中获取用户名     */    public String getUsernameFromToken(String token) {        return getClaimFromToken(token, claims -> claims.get(SecurityConstant.CLAIM_KEY_USERNAME, String.class));    }    /**     * 从Token中获取用户ID     */    public Long getUserIdFromToken(String token) {        return getClaimFromToken(token, claims -> claims.get(SecurityConstant.CLAIM_KEY_USER_ID, Long.class));    }    /**     * 从Token中获取Claims     */    public Claims getClaimsFromToken(String token) {        return Jwts.parserBuilder()                .setSigningKey(getSecretKey())                .build()                .parseClaimsJws(token)                .getBody();    }    /**     * 获取Token剩余有效时间(秒)     */    public Long getRemainingSeconds(String token) {        try {            Claims claims = getClaimsFromToken(token);            Date expiration = claims.getExpiration();            long remainingMillis = expiration.getTime() - System.currentTimeMillis();            return remainingMillis > 0 ? remainingMillis / 1000 : 0;        } catch (Exception e) {            return 0L;        }    }    /**     * 获取密钥     */    private SecretKey getSecretKey() {        return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));    }    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {        Claims claims = getClaimsFromToken(token);        return claimsResolver.apply(claims);    }}

2.4 认证服务实现

AuthServiceImpl.java(核心业务逻辑)

package com.tech.auth.service.impl;import cn.hutool.core.util.IdUtil;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.tech.auth.domain.dto.LoginDTO;import com.tech.auth.domain.dto.RegisterDTO;import com.tech.auth.domain.entity.User;import com.tech.auth.domain.vo.LoginVO;import com.tech.auth.domain.vo.TokenVO;import com.tech.auth.enums.UserStatusEnum;import com.tech.auth.mapper.UserMapper;import com.tech.auth.security.component.JwtTokenProvider;import com.tech.auth.security.constant.SecurityConstant;import com.tech.auth.service.AuthService;import com.tech.auth.service.CaptchaService;import com.tech.auth.service.PermissionService;import com.tech.auth.util.PasswordEncoder;import com.tech.common.constant.CacheConstant;import com.tech.common.exception.BusinessException;import com.tech.common.utils.IpUtil;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;import java.time.LocalDateTime;import java.util.List;import java.util.concurrent.TimeUnit;@Slf4j@Service@RequiredArgsConstructorpublic class AuthServiceImpl implements AuthService {    private final UserMapper userMapper;    private final JwtTokenProvider jwtTokenProvider;    private final PasswordEncoder passwordEncoder;    private final CaptchaService captchaService;    private final PermissionService permissionService;    private final RedisTemplate<String, Object> redisTemplate;    private final HttpServletRequest request;    @Override    @Transactional(rollbackFor = Exception.class)    public LoginVO login(LoginDTO loginDTO) {        // 1. 验证验证码        if (!captchaService.verifyCaptcha(loginDTO.getCaptchaKey(), loginDTO.getCaptcha())) {            throw new BusinessException("验证码错误或已过期");        }        // 2. 检查登录尝试次数        String loginAttemptKey = SecurityConstant.LOGIN_ATTEMPT_KEY + loginDTO.getUsername();        Integer attemptCount = (Integer) redisTemplate.opsForValue().get(loginAttemptKey);        if (attemptCount != null && attemptCount >= SecurityConstant.MAX_LOGIN_ATTEMPTS) {            throw new BusinessException("登录失败次数过多,请稍后再试");        }        // 3. 查询用户        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();        wrapper.eq(User::getUsername, loginDTO.getUsername())               .ne(User::getStatus, UserStatusEnum.DELETED.getCode());        User user = userMapper.selectOne(wrapper);        if (user == null) {            incrementLoginAttempt(loginAttemptKey);            throw new BusinessException("用户名或密码错误");        }        // 4. 检查用户状态        if (UserStatusEnum.DISABLED.equals(user.getStatus())) {            throw new BusinessException("账户已被禁用");        }        if (UserStatusEnum.LOCKED.equals(user.getStatus())) {            throw new BusinessException("账户已被锁定");        }        // 5. 验证密码        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {            incrementLoginAttempt(loginAttemptKey);            throw new BusinessException("用户名或密码错误");        }        // 6. 密码正确,清除登录尝试计数        redisTemplate.delete(loginAttemptKey);        // 7. 更新登录信息        String ip = IpUtil.getClientIp(request);        user.setLastLoginIp(ip);        user.setLastLoginTime(LocalDateTime.now());        userMapper.updateById(user);        // 8. 生成Token        String accessToken = jwtTokenProvider.generateAccessToken(user.getUsername(), user.getId());        String refreshToken = jwtTokenProvider.generateRefreshToken(user.getUsername());        // 9. 获取用户权限        List<String> permissions = permissionService.getUserPermissions(user.getId());        List<String> roles = permissionService.getUserRoles(user.getId());        // 10. 将Token加入缓存        String tokenKey = CacheConstant.USER_TOKEN_CACHE + user.getId();        redisTemplate.opsForValue().set(tokenKey, accessToken, SecurityConstant.TOKEN_EXPIRE, TimeUnit.SECONDS);        // 11. 返回登录结果        LoginVO loginVO = new LoginVO();        loginVO.setUserId(user.getId());        loginVO.setUsername(user.getUsername());        loginVO.setNickname(user.getNickname());        loginVO.setAccessToken(accessToken);        loginVO.setRefreshToken(refreshToken);        loginVO.setExpiresIn(SecurityConstant.TOKEN_EXPIRE);        loginVO.setRoles(roles);        loginVO.setPermissions(permissions);        loginVO.setLastLoginIp(ip);        loginVO.setLastLoginTime(LocalDateTime.now());        log.info("用户 {} 登录成功", user.getUsername());        return loginVO;    }    @Override    @Transactional(rollbackFor = Exception.class)    public Long register(RegisterDTO registerDTO) {        // 1. 验证验证码        if (!captchaService.verifyCaptcha(registerDTO.getCaptchaKey(), registerDTO.getCaptcha())) {            throw new BusinessException("验证码错误或已过期");        }        // 2. 验证两次密码是否一致        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {            throw new BusinessException("两次输入的密码不一致");        }        // 3. 检查用户名是否已存在        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();        wrapper.eq(User::getUsername, registerDTO.getUsername());        if (userMapper.selectCount(wrapper) > 0) {            throw new BusinessException("用户名已存在");        }        // 4. 检查手机号是否已存在        if (StringUtils.hasText(registerDTO.getPhone())) {            wrapper = new LambdaQueryWrapper<>();            wrapper.eq(User::getPhone, registerDTO.getPhone());            if (userMapper.selectCount(wrapper) > 0) {                throw new BusinessException("手机号已存在");            }        }        // 5. 检查邮箱是否已存在        if (StringUtils.hasText(registerDTO.getEmail())) {            wrapper = new LambdaQueryWrapper<>();            wrapper.eq(User::getEmail, registerDTO.getEmail());            if (userMapper.selectCount(wrapper) > 0) {                throw new BusinessException("邮箱已存在");            }        }        // 6. 创建用户        User user = new User();        user.setUsername(registerDTO.getUsername());        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));        user.setNickname(registerDTO.getNickname());        user.setPhone(registerDTO.getPhone());        user.setEmail(registerDTO.getEmail());        user.setStatus(UserStatusEnum.ENABLED);        userMapper.insert(user);        log.info("用户 {} 注册成功,用户ID: {}", registerDTO.getUsername(), user.getId());        return user.getId();    }    @Override    public void logout(String token) {        if (!StringUtils.hasText(token)) {            return;        }        try {            // 从Token中获取用户ID            Long userId = jwtTokenProvider.getUserIdFromToken(token);            if (userId != null) {                // 将Token加入黑名单                String tokenKey = CacheConstant.USER_TOKEN_CACHE + userId;                redisTemplate.delete(tokenKey);                log.info("用户 {} 已注销", userId);            }        } catch (Exception e) {            log.error("用户注销失败", e);        }    }    @Override    public TokenVO refreshToken(String refreshToken) {        // 验证Refresh Token        if (!jwtTokenProvider.validateToken(refreshToken)) {            throw new BusinessException("Refresh Token无效或已过期");        }        // 从Refresh Token中获取用户名        String username = jwtTokenProvider.getUsernameFromToken(refreshToken);        if (!StringUtils.hasText(username)) {            throw new BusinessException("Refresh Token解析失败");        }        // 查询用户        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();        wrapper.eq(User::getUsername, username)               .eq(User::getStatus, UserStatusEnum.ENABLED.getCode());        User user = userMapper.selectOne(wrapper);        if (user == null) {            throw new BusinessException("用户不存在或已被禁用");        }        // 生成新的Access Token        String newAccessToken = jwtTokenProvider.generateAccessToken(user.getUsername(), user.getId());        // 更新Token缓存        String tokenKey = CacheConstant.USER_TOKEN_CACHE + user.getId();        redisTemplate.opsForValue().set(tokenKey, newAccessToken, SecurityConstant.TOKEN_EXPIRE, TimeUnit.SECONDS);        // 返回新的Token        TokenVO tokenVO = new TokenVO();        tokenVO.setAccessToken(newAccessToken);        tokenVO.setRefreshToken(refreshToken);        tokenVO.setExpiresIn(SecurityConstant.TOKEN_EXPIRE);        log.info("用户 {} 刷新Token成功", username);        return tokenVO;    }    /**     * 增加登录尝试次数     */    private void incrementLoginAttempt(String key) {        Integer count = (Integer) redisTemplate.opsForValue().get(key);        if (count == null) {            count = 1;        } else {            count++;        }        redisTemplate.opsForValue().set(key, count, SecurityConstant.LOGIN_ATTEMPT_EXPIRE, TimeUnit.SECONDS);        // 如果超过最大尝试次数,锁定账户        if (count >= SecurityConstant.MAX_LOGIN_ATTEMPTS) {            String lockKey = SecurityConstant.LOGIN_LOCK_KEY + key.substring(key.lastIndexOf(":") + 1);            redisTemplate.opsForValue().set(lockKey, "locked", SecurityConstant.LOGIN_LOCK_EXPIRE, TimeUnit.SECONDS);            log.warn("用户登录失败次数过多,账户已被锁定: {}", key);        }    }}

2.5 验证码服务

CaptchaServiceImpl.java

package com.tech.auth.service.impl;import com.github.whvcse.easy.captcha.support.CaptchaType;import com.tech.auth.service.CaptchaService;import com.tech.common.constant.CacheConstant;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Slf4j@Service@RequiredArgsConstructorpublic class CaptchaServiceImpl implements CaptchaService {    private final RedisTemplate<String, Object> redisTemplate;    @Value("${captcha.type:arithmetic}")    private String captchaType;    @Value("${captcha.width:130}")    private int width;    @Value("${captcha.height:48}")    private int height;    @Value("${captcha.length:2}")    private int length;    @Value("${captcha.expire-seconds:300}")    private long expireSeconds;    @Override    public String generateCaptcha(String key) {        // 生成验证码        String captcha = generateRandomCode();        // 存储到Redis        String cacheKey = CacheConstant.CAPTCHA_CACHE + key;        redisTemplate.opsForValue().set(cacheKey, captcha, expireSeconds, TimeUnit.SECONDS);        log.debug("生成验证码: key={}, code={}", key, captcha);        return captcha;    }    @Override    public boolean verifyCaptcha(String key, String code) {        if (key == null || code == null) {            return false;        }        // 从Redis获取验证码        String cacheKey = CacheConstant.CAPTCHA_CACHE + key;        String storedCode = (String) redisTemplate.opsForValue().get(cacheKey);        if (storedCode == null) {            log.warn("验证码已过期: key={}", key);            return false;        }        // 验证验证码(不区分大小写)        boolean success = storedCode.equalsIgnoreCase(code);        // 验证成功后删除验证码        if (success) {            redisTemplate.delete(cacheKey);            log.debug("验证码验证成功: key={}", key);        } else {            log.warn("验证码验证失败: key={}, input={}, stored={}", key, code, storedCode);        }        return success;    }    @Override    public void clearCaptcha(String key) {        String cacheKey = CacheConstant.CAPTCHA_CACHE + key;        redisTemplate.delete(cacheKey);    }    /**     * 生成随机验证码     */    private String generateRandomCode() {        switch (captchaType.toLowerCase()) {            case "arithmetic":                return generateArithmeticCode();            case "chinese":                return generateChineseCode();            default:                return generateRandomStringCode();        }    }    /**     * 生成算术验证码     */    private String generateArithmeticCode() {        int a = (int) (Math.random() * 10) + 1;        int b = (int) (Math.random() * 10) + 1;        int result = a + b;        return String.valueOf(result);    }    /**     * 生成中文验证码     */    private String generateChineseCode() {        String[] chineseChars = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};        int num = (int) (Math.random() * 100);        if (num < 10) {            return chineseChars[num];        } else if (num < 20) {            return "十" + (num % 10 == 0 ? "" : chineseChars[num % 10]);        } else {            return chineseChars[num / 10] + "十" + (num % 10 == 0 ? "" : chineseChars[num % 10]);        }    }    /**     * 生成随机字符串验证码     */    private String generateRandomStringCode() {        String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";        StringBuilder code = new StringBuilder();        for (int i = 0; i < length; i++) {            code.append(chars.charAt((int) (Math.random() * chars.length())));        }        return code.toString();    }}

2.6 控制器

AuthController.java

package com.tech.auth.controller;import com.tech.auth.domain.dto.LoginDTO;import com.tech.auth.domain.dto.RegisterDTO;import com.tech.auth.domain.vo.LoginVO;import com.tech.auth.domain.vo.TokenVO;import com.tech.auth.service.AuthService;import com.tech.common.domain.vo.ApiResult;import io.swagger.v3.oas.annotations.Operation;import io.swagger.v3.oas.annotations.tags.Tag;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;import javax.validation.Valid;@Slf4j@Tag(name = "认证管理")@RestController@RequestMapping("/auth")@RequiredArgsConstructorpublic class AuthController {    private final AuthService authService;    private final HttpServletRequest request;    @Operation(summary = "用户登录")    @PostMapping("/login")    public ApiResult<LoginVO> login(@Valid @RequestBody LoginDTO loginDTO) {        LoginVO loginVO = authService.login(loginDTO);        return ApiResult.success("登录成功", loginVO);    }    @Operation(summary = "用户注册")    @PostMapping("/register")    public ApiResult<Long> register(@Valid @RequestBody RegisterDTO registerDTO) {        Long userId = authService.register(registerDTO);        return ApiResult.success("注册成功", userId);    }    @Operation(summary = "用户注销")    @PostMapping("/logout")    public ApiResult<Void> logout() {        String token = getTokenFromRequest();        authService.logout(token);        return ApiResult.success("注销成功");    }    @Operation(summary = "刷新Token")    @PostMapping("/refresh-token")    public ApiResult<TokenVO> refreshToken(@RequestParam String refreshToken) {        TokenVO tokenVO = authService.refreshToken(refreshToken);        return ApiResult.success("Token刷新成功", tokenVO);    }    @Operation(summary = "获取验证码")    @GetMapping("/captcha")    public ApiResult<String> getCaptcha(@RequestParam String key) {        String captcha = authService.getCaptcha(key);        return ApiResult.success(captcha);    }    /**     * 从请求中获取Token     */    private String getTokenFromRequest() {        String bearerToken = request.getHeader("Authorization");        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {            return bearerToken.substring(7);        }        return null;    }}


📊 第三步:测试认证中心

3.1 启动认证中心

# 进入认证中心模块cd microservice-auth

# 编译打包mvn clean package
# 运行java -jar target/microservice-auth-1.0.0.jar

3.2 API测试

测试1:获取验证码
curl "http://localhost:8081/auth-service/auth/captcha?key=login:123456"

测试2:用户注册
curl -X POST "http://localhost:8081/auth-service/auth/register" \  -H "Content-Type: application/json" \  -d '{    "username": "testuser",    "password": "Test@123456",    "confirmPassword": "Test@123456",    "nickname": "测试用户",    "phone": "13800138001",    "email": "test@example.com",    "captcha": "123456",    "captchaKey": "captcha:register:abc123"  }'

测试3:用户登录
curl -X POST "http://localhost:8081/auth-service/auth/login" \  -H "Content-Type: application/json" \  -d '{    "username": "testuser",    "password": "Test@123456",    "captcha": "123456",    "captchaKey": "captcha:login:abc123"  }'

响应示例:

{  "code": 200,  "message": "登录成功",  "data": {    "userId": 1,    "username": "testuser",    "nickname": "测试用户",    "accessToken": "eyJhbGciOiJIUzUxMiJ9...",    "refreshToken": "eyJhbGciOiJIUzUxMiJ9...",    "tokenType": "Bearer",    "expiresIn": 7200,    "roles": ["ROLE_USER"],    "permissions": ["system:user:query"],    "lastLoginIp": "127.0.0.1",    "lastLoginTime": "2026-01-01 12:00:00"  },  "timestamp": 1735718400000}

测试4:访问受保护接口
# 使用Token访问curl -X GET "http://localhost:8081/auth-service/users/1" \  -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9..."

测试5:刷新Token
curl -X POST "http://localhost:8081/auth-service/auth/refresh-token" \  -H "Content-Type: application/x-www-form-urlencoded" \  -d "refreshToken=eyJhbGciOiJIUzUxMiJ9..."


🔐 第四步:安全增强配置

4.1 密码策略配置

PasswordPolicyConfig.java

package com.tech.auth.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;@Configurationpublic class PasswordPolicyConfig {    /**     * 密码加密器配置     */    @Bean    public PasswordEncoder passwordEncoder() {        // 强度设置为10,兼顾安全性和性能        return new BCryptPasswordEncoder(10);    }    /**     * 密码策略验证     */    public static boolean validatePassword(String password) {        if (password == null || password.length() < 8) {            return false;        }        // 检查是否包含数字        if (!password.matches(".*\\d.*")) {            return false;        }        // 检查是否包含小写字母        if (!password.matches(".*[a-z].*")) {            return false;        }        // 检查是否包含大写字母        if (!password.matches(".*[A-Z].*")) {            return false;        }        // 检查是否包含特殊字符        if (!password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*")) {            return false;        }        return true;    }}

4.2 登录安全配置

LoginSecurityConfig.java

package com.tech.auth.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;@Configurationpublic class LoginSecurityConfig {    @Value("${security.login.max-attempts:5}")    private int maxLoginAttempts;    @Value("${security.login.lock-duration:1800}")    private int lockDuration;    @Value("${security.session.timeout:1800}")    private int sessionTimeout;    /**     * 获取最大登录尝试次数     */    public int getMaxLoginAttempts() {        return maxLoginAttempts;    }    /**     * 获取账户锁定时间(秒)     */    public int getLockDuration() {        return lockDuration;    }    /**     * 获取会话超时时间(秒)     */    public int getSessionTimeout() {        return sessionTimeout;    }}


📈 第五步:监控和日志

5.1 登录日志记录

LoginLogAspect.java

package com.tech.auth.aspect;import com.tech.auth.domain.entity.User;import com.tech.common.utils.IpUtil;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.time.LocalDateTime;@Slf4j@Aspect@Componentpublic class LoginLogAspect {    /**     * 登录成功日志     */    @AfterReturning(pointcut = "execution(* com.tech.auth.service.impl.AuthServiceImpl.login(..))",                     returning = "result")    public void afterLoginSuccess(JoinPoint joinPoint, Object result) {        try {            Object[] args = joinPoint.getArgs();            if (args.length > 0 && args[0] instanceof com.tech.auth.domain.dto.LoginDTO) {                com.tech.auth.domain.dto.LoginDTO loginDTO = (com.tech.auth.domain.dto.LoginDTO) args[0];                String username = loginDTO.getUsername();                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder                        .getRequestAttributes()).getRequest();                String ip = IpUtil.getClientIp(request);                String userAgent = request.getHeader("User-Agent");                // 记录登录成功日志                log.info("登录成功 - 用户: {}, IP: {}, 时间: {}, User-Agent: {}",                         username, ip, LocalDateTime.now(), userAgent);                // 这里可以保存到数据库                // loginLogService.saveSuccessLog(username, ip, userAgent);            }        } catch (Exception e) {            log.error("记录登录成功日志失败", e);        }    }    /**     * 登录失败日志     */    @AfterThrowing(pointcut = "execution(* com.tech.auth.service.impl.AuthServiceImpl.login(..))",                    throwing = "exception")    public void afterLoginFailure(JoinPoint joinPoint, Exception exception) {        try {            Object[] args = joinPoint.getArgs();            if (args.length > 0 && args[0] instanceof com.tech.auth.domain.dto.LoginDTO) {                com.tech.auth.domain.dto.LoginDTO loginDTO = (com.tech.auth.domain.dto.LoginDTO) args[0];                String username = loginDTO.getUsername();                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder                        .getRequestAttributes()).getRequest();                String ip = IpUtil.getClientIp(request);                String userAgent = request.getHeader("User-Agent");                String errorMsg = exception.getMessage();                // 记录登录失败日志                log.warn("登录失败 - 用户: {}, IP: {}, 时间: {}, 原因: {}, User-Agent: {}",                         username, ip, LocalDateTime.now(), errorMsg, userAgent);                // 这里可以保存到数据库                // loginLogService.saveFailureLog(username, ip, errorMsg, userAgent);            }        } catch (Exception e) {            log.error("记录登录失败日志失败", e);        }    }}


🎯 第六步:集成测试

6.1 单元测试

AuthServiceTest.java

package com.tech.auth.service;import com.tech.auth.AuthApplication;import com.tech.auth.domain.dto.LoginDTO;import com.tech.auth.domain.dto.RegisterDTO;import com.tech.auth.domain.vo.LoginVO;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.annotation.Rollback;import org.springframework.transaction.annotation.Transactional;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest(classes = AuthApplication.class)@Transactional@Rollbackclass AuthServiceTest {    @Autowired    private AuthService authService;    @Test    void testRegisterAndLogin() {        // 1. 注册用户        RegisterDTO registerDTO = new RegisterDTO();        registerDTO.setUsername("testuser");        registerDTO.setPassword("Test@123456");        registerDTO.setConfirmPassword("Test@123456");        registerDTO.setNickname("测试用户");        registerDTO.setPhone("13800138001");        registerDTO.setEmail("test@example.com");        registerDTO.setCaptcha("123456");        registerDTO.setCaptchaKey("test:register");        Long userId = authService.register(registerDTO);        assertNotNull(userId);        // 2. 用户登录        LoginDTO loginDTO = new LoginDTO();        loginDTO.setUsername("testuser");        loginDTO.setPassword("Test@123456");        loginDTO.setCaptcha("123456");        loginDTO.setCaptchaKey("test:login");        LoginVO loginVO = authService.login(loginDTO);        assertNotNull(loginVO);        assertEquals("testuser", loginVO.getUsername());        assertNotNull(loginVO.getAccessToken());        assertNotNull(loginVO.getRefreshToken());    }    @Test    void testLoginWithWrongPassword() {        LoginDTO loginDTO = new LoginDTO();        loginDTO.setUsername("admin");        loginDTO.setPassword("wrongpassword");        loginDTO.setCaptcha("123456");        loginDTO.setCaptchaKey("test:login");        assertThrows(Exception.class, () -> {            authService.login(loginDTO);        });    }}


📊 认证中心功能总结

功能模块

实现技术

特点

用户认证

Spring Security + JWT

无状态认证,支持分布式部署

密码加密

BCryptPasswordEncoder

强度可调,安全性高

权限管理

RBAC模型

支持角色、权限多级授权

验证码

Redis缓存

多种类型,防暴力破解

登录保护

Redis计数

防止暴力破解,自动锁定

会话管理

JWT + Redis

分布式会话,Token自动续期

安全审计

AOP切面

完整登录日志记录

跨域支持

CORS配置

支持前后端分离部署

API文档

Knife4j

在线API文档,调试支持


🔮 下期预告

第五章:搭建用户服务(microservice-user)

我们将实现:

  • ✅ 用户信息管理

  • ✅ 用户资料维护

  • ✅ 头像上传

  • ✅ 用户统计

  • ✅ 消息通知

  • ✅ 积分系统


💡 最佳实践建议

  1. 安全第一

    • 使用强密码策略

    • 启用HTTPS

    • 定期更换JWT密钥

    • 实施多因素认证

  2. 性能优化

    • 合理设置Token过期时间

    • 使用Redis缓存权限信息

    • 数据库索引优化

    • 连接池配置

  3. 监控告警

    • 监控登录失败次数

    • 记录异常登录行为

    • 设置Token使用告警

    • 审计日志保存

  4. 可扩展性

    • 支持多端登录

    • 支持第三方登录

    • 支持单点登录

    • 支持OAuth2.0


技术要点:Spring Security、JWT、Redis、MySQL、RBAC

安全等级:企业级安全标准

适用场景:电商、金融、企业管理系统


马年奔腾,安全护航! 🎉

认证中心是微服务架构的安全基石,一个设计良好的认证系统能够让整个架构更加稳固可靠。希望这篇文章能够帮助你快速搭建自己的认证中心!

点赞、收藏、转发,让更多开发者受益!

关注我,获取更多微服务实战教程!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐