登录流程设计

登录实现

登录流程是 Web 应用的核心功能,核心目标是:验证用户身份合法性维护登录状态保障访问安全。Java 中实现登录流程的方式多样,根据技术栈和场景可分为以下几类,涵盖从原生实现到框架集成,从有状态到无状态:

一、基于 Servlet 原生实现(理解底层原理)

适合简单 Web 应用,直接使用 Servlet API 处理请求、验证身份、管理 Session,适合理解登录流程的底层逻辑。

核心流程

  1. 前端提交用户名 / 密码到后端 Servlet;

  2. 后端接收参数,与数据库中存储的用户信息(密码需加密)比对;

  3. 验证通过:创建 Session,存储用户信息(如用户 ID、角色),返回登录成功;

  4. 验证失败:返回错误信息(如 “用户名或密码错误”)。

实现代码

1. 实体类(User)

java

运行

public class User {
    private Integer id;
    private String username;
    private String password; // 存储加密后的密码(如BCrypt加密)
    // get/set
}
2. 数据访问层(模拟数据库查询)

java

运行

public class UserDao {
    // 模拟数据库:key=用户名,value=用户对象(实际项目中从数据库查询)
    private static Map<String, User> userMap = new HashMap<>();
​
    static {
        // 初始化用户:密码用BCrypt加密(原始密码123456)
        String encryptedPwd = BCrypt.hashpw("123456", BCrypt.gensalt());
        userMap.put("zhangsan", new User(1, "zhangsan", encryptedPwd));
    }
​
    // 根据用户名查询用户
    public User findByUsername(String username) {
        return userMap.get(username);
    }
}
3. 登录 Servlet

java

运行

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private UserDao userDao = new UserDao();
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获取前端参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
​
        // 2. 验证参数非空
        if (username == null || password == null) {
            resp.getWriter().write("用户名或密码不能为空");
            return;
        }
​
        // 3. 查询用户
        User user = userDao.findByUsername(username);
        if (user == null) {
            resp.getWriter().write("用户名不存在");
            return;
        }
​
        // 4. 验证密码(BCrypt解密比对)
        if (!BCrypt.checkpw(password, user.getPassword())) {
            resp.getWriter().write("密码错误");
            return;
        }
​
        // 5. 验证通过:创建Session,存储用户信息(避免存储敏感信息)
        HttpSession session = req.getSession();
        session.setAttribute("userId", user.getId());
        session.setAttribute("username", user.getUsername());
        session.setMaxInactiveInterval(3600); // Session有效期1小时
​
        // 6. 返回成功(实际项目中可能跳转页面或返回JSON)
        resp.getWriter().write("登录成功");
    }
}
4. 登出 Servlet

java

运行

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 销毁Session
        HttpSession session = req.getSession();
        session.invalidate();
        resp.getWriter().write("登出成功");
    }
}

核心技术点

  • 密码加密:使用BCrypt(不可逆加密,带盐值),避免存储明文密码(依赖org.mindrot:jbcrypt:0.4);

  • Session 管理:通过HttpSession存储登录状态,依赖 Servlet 容器(如 Tomcat)的 Session 存储(默认内存,分布式场景需优化);

  • 安全性:需通过HTTPS传输避免密码泄露,可添加验证码防止暴力破解。

优缺点

  • 优点:原理清晰,无框架依赖,适合学习和简单应用;

  • 缺点:需手动处理参数校验、异常、Session 分布式问题,功能扩展繁琐(如权限控制)。

二、Spring MVC + Session(企业级基础实现)

基于 Spring MVC 框架简化开发,使用注解接收请求,依赖 Spring 的 IoC 管理组件,保留 Session 有状态登录,适合中小型 Web 应用。

核心流程

  1. 前端通过表单 / JSON 提交用户名 / 密码到@PostMapping("/login")接口;

  2. 后端 Controller 接收参数,调用 Service 层验证;

  3. 验证通过:将用户信息存入HttpSession,返回登录成功(如 JWT 令牌或 SessionId);

  4. 验证失败:返回错误信息(通过@ControllerAdvice统一异常处理)。

实现代码

1. 依赖(Maven)

xml

<dependencies>
    <!-- Spring MVC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- BCrypt加密 -->
    <dependency>
        <groupId>org.mindrot</groupId>
        <artifactId>jbcrypt</artifactId>
        <version>0.4</version>
    </dependency>
</dependencies>
2. Controller 层

java

运行

@RestController
public class LoginController {
    @Autowired
    private UserService userService;
​
    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO, HttpSession session) {
        // 调用Service验证
        User user = userService.login(loginDTO.getUsername(), loginDTO.getPassword());
        // 存储用户信息到Session(仅存必要字段)
        session.setAttribute("loginUser", user.getId());
        session.setMaxInactiveInterval(3600);
        return Result.success("登录成功");
    }
​
    @GetMapping("/logout")
    public Result logout(HttpSession session) {
        session.invalidate();
        return Result.success("登出成功");
    }
​
    // 测试登录状态:需登录才能访问
    @GetMapping("/user/info")
    public Result getUserInfo(HttpSession session) {
        Integer userId = (Integer) session.getAttribute("loginUser");
        if (userId == null) {
            return Result.fail("未登录");
        }
        User user = userService.findById(userId);
        return Result.success(user);
    }
}
​
// 数据传输对象(DTO)
@Data
class LoginDTO {
    private String username;
    private String password;
}
​
// 统一返回结果
@Data
class Result {
    private int code;
    private String msg;
    private Object data;
    // 静态success/fail方法省略
}
3. Service 层(核心验证逻辑)

java

运行

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper; // 假设通过MyBatis访问数据库
​
    public User login(String username, String password) {
        // 1. 查询用户
        User user = userMapper.selectByUsername(username);
        if (user == null) {
            throw new BusinessException("用户名不存在");
        }
        // 2. 验证密码(BCrypt比对)
        if (!BCrypt.checkpw(password, user.getPassword())) {
            throw new BusinessException("密码错误");
        }
        return user;
    }
​
    public User findById(Integer userId) {
        return userMapper.selectById(userId);
    }
}
4. 登录拦截器(验证登录状态)

java

运行

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if (session.getAttribute("loginUser") == null) {
            // 未登录:返回401
            response.setStatus(401);
            response.getWriter().write("请先登录");
            return false;
        }
        return true;
    }
}
​
// 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/login", "/logout"); // 排除登录/登出接口
    }
}

核心技术点

  • Spring MVC 注解@PostMapping接收请求,@RequestBody绑定 JSON 参数,简化参数处理;

  • 拦截器:通过HandlerInterceptor统一验证登录状态,避免在每个接口重复判断;

  • 异常处理:自定义BusinessException,结合@ControllerAdvice统一返回错误信息。

优缺点

  • 优点:开发效率高,Spring 生态支持完善,适合中小型 Web 应用;

  • 缺点:依赖 Session(有状态),分布式部署时需解决 Session 共享问题(如 Redis 存储 Session)。

三、Spring Security(企业级安全框架)

Spring Security 是功能完善的安全框架,内置登录验证、Session 管理、权限控制等功能,适合复杂企业级应用(需权限管理、记住我等高级特性)。

核心流程

  1. 框架自动拦截/login请求,接收用户名 / 密码;

  2. 通过UserDetailsService查询用户信息,PasswordEncoder验证密码;

  3. 验证通过:生成 Session(或 JWT),触发登录成功事件;

  4. 验证失败:返回错误页面 / 信息(可自定义)。

实现代码

1. 依赖(Spring Boot)

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 配置类(核心)

java

运行

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
​
    // 密码编码器(BCrypt)
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
​
    // 自定义用户查询服务
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            // 模拟查询用户(实际从数据库获取)
            if ("zhangsan".equals(username)) {
                // 密码:123456(已加密)
                String encryptedPwd = passwordEncoder().encode("123456");
                // 返回用户信息(包含用户名、密码、权限)
                return User.withUsername("zhangsan")
                        .password(encryptedPwd)
                        .roles("USER")
                        .build();
            }
            throw new UsernameNotFoundException("用户名不存在");
        };
    }
​
    // 配置安全规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // 开发环境关闭CSRF(生产环境需开启)
            .authorizeRequests()
                .antMatchers("/login", "/logout").permitAll() // 登录/登出接口允许匿名访问
                .anyRequest().authenticated() // 其他接口需登录
            .and()
            .formLogin() // 启用表单登录
                .loginProcessingUrl("/login") // 登录提交地址
                .successHandler((req, resp, auth) -> { // 登录成功处理
                    resp.setContentType("application/json;charset=utf-8");
                    resp.getWriter().write("{\"code\":200,\"msg\":\"登录成功\"}");
                })
                .failureHandler((req, resp, e) -> { // 登录失败处理
                    resp.setContentType("application/json;charset=utf-8");
                    resp.getWriter().write("{\"code\":400,\"msg\":\"" + e.getMessage() + "\"}");
                })
            .and()
            .logout() // 登出配置
                .logoutUrl("/logout")
                .logoutSuccessHandler((req, resp, auth) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    resp.getWriter().write("{\"code\":200,\"msg\":\"登出成功\"}");
                });
    }
}
3. 测试接口(需登录)

java

运行

@RestController
public class UserController {
    // 登录后可访问
    @GetMapping("/user/info")
    public String getUserInfo(Authentication authentication) {
        // 获取当前登录用户信息
        String username = authentication.getName();
        return "当前登录用户:" + username;
    }
}

核心技术点

  • UserDetailsService:框架回调该接口查询用户信息(用户名、加密密码、权限);

  • PasswordEncoder:统一密码加密 / 验证策略(默认BCryptPasswordEncoder);

  • 安全配置:通过HttpSecurity配置哪些接口需要登录、登录 / 登出的处理逻辑、CSRF 防护等。

优缺点

  • 优点:功能全面(支持记住我、验证码、OAuth2.0 等),安全性高(内置防 XSS、CSRF 等),适合复杂企业应用;

  • 缺点:学习成本高,配置较繁琐,简单应用可能 “过重”。

四、JWT 无状态登录(分布式系统)

适合分布式 / 微服务架构,通过 JWT(JSON Web Token)存储用户信息,服务器不保存 Session,避免 Session 共享问题。

核心流程

  1. 前端提交用户名 / 密码,后端验证通过后生成 JWT 令牌(包含用户 ID、过期时间等,签名防篡改);

  2. 前端存储 JWT(如 localStorage),后续请求在 Header 中携带Authorization: Bearer {token}

  3. 后端通过过滤器验证 JWT 合法性(签名、过期时间),解析用户信息,允许访问;

  4. 登出:前端删除 JWT(服务器无需处理,因无状态)。

实现代码

1. 依赖(JWT 工具)

xml

<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>
2. JWT 工具类(生成、验证、解析)

java

运行

@Component
public class JwtUtils {
    // 密钥(生产环境需复杂且保密)
    private static final String SECRET = "your-secret-key-123456";
    // 过期时间:2小时
    private static final long EXPIRATION = 2 * 60 * 60 * 1000;
​
    // 生成JWT
    public String generateToken(Integer userId, String username) {
        return Jwts.builder()
                .setSubject(userId.toString()) // 存储用户ID
                .claim("username", username) // 附加用户名
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 过期时间
                .signWith(SignatureAlgorithm.HS256, SECRET) // 签名算法+密钥
                .compact();
    }
​
    // 验证JWT并解析用户ID
    public Integer getUserIdFromToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(SECRET)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
            return Integer.parseInt(claims.getSubject());
        } catch (Exception e) {
            // 解析失败(过期、签名错误等)
            throw new BusinessException("令牌无效或已过期");
        }
    }
}
3. 登录接口(生成 JWT)

java

运行

@RestController
public class JwtLoginController {
    @Autowired
    private UserService userService;
    @Autowired
    private JwtUtils jwtUtils;
​
    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO) {
        User user = userService.login(loginDTO.getUsername(), loginDTO.getPassword());
        // 生成JWT
        String token = jwtUtils.generateToken(user.getId(), user.getUsername());
        return Result.success(token);
    }
}
4. JWT 过滤器(验证令牌)

java

运行

@Component
public class JwtFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtils jwtUtils;
​
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 1. 获取请求头中的token
        String token = request.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            response.setStatus(401);
            response.getWriter().write("请先登录(未携带令牌)");
            return;
        }
        token = token.substring(7); // 去掉"Bearer "前缀
​
        // 2. 验证token并解析用户ID
        Integer userId;
        try {
            userId = jwtUtils.getUserIdFromToken(token);
        } catch (BusinessException e) {
            response.setStatus(401);
            response.getWriter().write(e.getMessage());
            return;
        }
​
        // 3. 将用户ID存入请求属性,供后续接口使用
        request.setAttribute("userId", userId);
​
        // 4. 继续执行过滤器链
        filterChain.doFilter(request, response);
    }
}
​
// 注册过滤器
@Configuration
public class JwtConfig {
    @Bean
    public FilterRegistrationBean<JwtFilter> jwtFilter() {
        FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new JwtFilter());
        registrationBean.addUrlPatterns("/**"); // 拦截所有请求
        registrationBean.addInitParameter("excludeUrls", "/login,/logout"); // 排除登录接口
        return registrationBean;
    }
}

核心技术点

  • JWT 结构:由Header(算法)、Payload(用户信息 + 过期时间)、Signature(签名,防篡改)三部分组成;

  • 无状态:服务器不存储登录状态,令牌本身包含所有必要信息,适合分布式系统;

  • 安全性:密钥必须保密,令牌需通过 HTTPS 传输,避免前端存储敏感信息(如密码)。

优缺点

  • 优点:适合分布式 / 微服务架构,无 Session 共享问题,扩展性好;

  • 缺点:令牌无法主动失效(除非服务器维护黑名单),过期时间需合理设置(过长不安全,过短影响体验)。

五、OAuth2.0 第三方登录(社交登录)

通过第三方平台(如微信、QQ、GitHub)的身份认证实现登录,无需用户注册,提升用户体验。

核心流程(以 GitHub 登录为例)

  1. 前端点击 “GitHub 登录”,跳转至 GitHub 授权页;

  2. 用户授权后,GitHub 回调后端接口,携带code

  3. 后端用code向 GitHub 换取access_token

  4. access_token获取 GitHub 用户信息(如用户名、邮箱);

  5. 后端关联本地用户(新用户自动注册),生成登录状态(Session 或 JWT)。

实现代码(Spring Security OAuth2.0)

1. 依赖

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
2. 配置文件(application.yml)

yaml

spring:
  security:
    oauth2:
      client:
        registration:
          github: # GitHub登录配置
            client-id: 你的GitHub客户端ID(从GitHub开发者平台申请)
            client-secret: 你的GitHub客户端密钥
            scope: user:email # 申请的权限(获取用户邮箱)
        provider:
          github:
            user-info-uri: https://api.github.com/user # GitHub用户信息接口
3. 安全配置

java

运行

@Configuration
@EnableWebSecurity
public class OAuth2Config extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .oauth2Login() // 启用OAuth2登录
                .defaultSuccessUrl("/login/success", true) // 登录成功跳转地址
                .userInfoEndpoint()
                .userService(oauth2UserService()); // 自定义用户信息处理
    }
​
    // 自定义用户信息处理(关联本地用户)
    @Bean
    public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        return request -> {
            // 1. 调用默认服务获取GitHub用户信息
            OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
            OAuth2User oauth2User = delegate.loadUser(request);
​
            // 2. 解析GitHub用户信息(如用户名、邮箱)
            Map<String, Object> attributes = oauth2User.getAttributes();
            String githubUsername = (String) attributes.get("login");
            String email = (String) attributes.get("email");
​
            // 3. 关联本地用户(新用户自动注册)
            User localUser = userService.findByThirdPartyId("github_" + githubUsername);
            if (localUser == null) {
                localUser = new User();
                localUser.setUsername(githubUsername);
                localUser.setThirdPartyId("github_" + githubUsername);
                userService.register(localUser); // 自动注册
            }
​
            // 4. 返回自定义用户信息(包含本地用户ID)
            return new DefaultOAuth2User(
                Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")),
                attributes,
                "login"
            );
        };
    }
}
4. 登录成功接口

java

运行

@RestController
public class OAuth2Controller {
    @GetMapping("/login/success")
    public Result loginSuccess(Authentication authentication) {
        // 获取登录用户信息(包含第三方平台信息)
        OAuth2User oauth2User = (OAuth2User) authentication.getPrincipal();
        String username = oauth2User.getAttribute("login");
        return Result.success("GitHub登录成功,用户:" + username);
    }
}

核心技术点

  • OAuth2.0 角色:资源所有者(用户)、客户端(你的应用)、授权服务器(GitHub)、资源服务器(存储用户信息的服务器);

  • 授权流程:常用Authorization Code模式(安全,适合 Web 应用),通过code换取access_token,再获取用户信息;

  • 用户关联:将第三方用户 ID(如github_xxx)与本地用户表关联,实现 “一次授权,多端登录”。

优缺点

  • 优点:无需用户注册,降低使用门槛,适合社交类、工具类应用;

  • 缺点:依赖第三方平台,需申请开发者账号,用户信息获取受第三方限制。

总结:不同方法的选择依据

实现方式核心特点适用场景
Servlet 原生底层原理,无框架依赖学习理解、极简应用
Spring MVC + Session简单易用,有状态中小型 Web 应用,单体架构
Spring Security功能全面,安全可靠企业级应用,需权限管理、复杂安全控制
JWT 无状态登录分布式友好,无 Session微服务 / 分布式架构,前后端分离应用
OAuth2.0 第三方登录社交登录,用户体验好需要快速获客的应用(如电商、工具类)

无论哪种方式,核心原则不变:密码必须加密存储传输需用 HTTPS登录状态需验证,根据项目规模和架构选择最合适的方案即可。


多 Java 登录实现方案对比与深度讲解

核心结论

不同登录方案的本质是「状态管理方式 + 技术栈适配」的选择:JWT 双 Token 适合前后端分离 / 分布式架构,Session 适合传统单体应用,Redis 替代 Session 解决分布式 Session 共享问题,通用模板适合多场景复用,过滤器校验适合传统 Web 的页面权限控制。以下从核心逻辑、关键差异、适用场景展开对比讲解。

一、各方案核心逻辑与亮点提炼

1. 方案 1:JWT 双 Token 登录(accessToken+refreshToken)

核心逻辑
  • 技术栈:Spring Boot + JWT + Spring Security PasswordEncoder(BCrypt 加密)

  • 流程:用户验证→状态校验→密码加密比对→生成双 Token(accessToken 短期访问 + refreshToken 长期刷新)→返回给前端

  • 关键设计:

    • accessToken(2 小时):日常接口访问,避免敏感信息,降低被盗风险

    • refreshToken(7 天):accessToken 过期后无感刷新,无需重复登录

    • 前端捕捉 401 异常,用 refreshToken 自动刷新 Token 并重试请求

核心亮点
  • 无状态:服务器不存储登录状态,Token 本身携带用户信息,适配分布式 / 微服务

  • 安全:双 Token 分离职责,密码 BCrypt 不可逆加密,签名防篡改

  • 体验好:Token 过期无感刷新,用户无需重复登录

2. 方案 2:通用登录模板(AbstractLoginService)

核心逻辑
  • 技术栈:泛型抽象类 + JWT + PasswordEncoder

  • 流程:通过抽象方法封装通用登录流程(校验→查询用户→状态校验→密码比对→Token 生成→结果封装),子类实现具体业务细节(如用户查询、状态规则)

  • 关键设计:

    • 泛型适配:支持不同 User 实体、DTO/VO 结构,无需重复编写登录流程

    • 钩子方法:预留前置校验、后置操作(如登录日志)扩展点

    • 配置分离:JWT 参数通过配置类注入,支持多环境切换

核心亮点
  • 高复用:一套模板适配多系统登录需求,减少重复开发

  • 扩展灵活:抽象方法 + 钩子方法,可定制业务细节(如多端登录、特殊状态校验)

  • 规范统一:强制登录流程标准化,避免团队开发的逻辑混乱

3. 方案 3:基于 Session 的登录

核心逻辑
  • 技术栈:Servlet/Spring MVC + HttpSession + ThreadLocal

  • 流程:发送验证码(存储到 Session)→ 登录校验(手机号 + 验证码)→ 查询 / 创建用户→ 存储用户信息到 Session→ 拦截器校验 Session 存在性

  • 关键设计:

    • 状态存储:Session 由 Servlet 容器(如 Tomcat)管理,默认内存存储

    • 线程隔离:用 ThreadLocal 存储当前用户信息,接口直接获取,无需重复从 Session 查询

    • 敏感信息隐藏:用 UserDTO 剥离敏感字段(如密码),避免前端泄露

核心亮点
  • 实现简单:无需额外中间件(如 Redis/JWT),依赖原生 API

  • 适合单体:Session 天然适配单体应用,无分布式同步成本

  • 安全性:基于 Cookie 的 JSESSIONID 传输,默认防 CSRF(需配合框架配置)

4. 方案 4:Redis 替代 Session 的登录

核心逻辑
  • 技术栈:Redis + UUID Token + 拦截器

  • 流程:发送验证码(存储到 Redis)→ 登录校验→ 查询 / 创建用户→ 生成 UUID Token→ 用 Hash 结构存储用户信息到 Redis→ 前端携带 Token 访问,拦截器校验 Redis 中用户存在性

  • 关键设计:

    • Key 设计:LOGIN_USER_KEY + Token(唯一标识用户),LOGIN_CODE_KEY + 手机号(存储验证码)

    • 过期时间:Redis 设置 Token 有效期(如 30 分钟),自动过期清理

    • 双拦截器:刷新 Token 拦截器(所有路径)+ 登录校验拦截器(需登录路径)

核心亮点
  • 解决 Session 共享:Redis 分布式存储,适配多 Tomcat 集群部署

  • 性能优:Redis 读写速度快,支持高并发,避免 Session 内存溢出

  • 灵活扩展:支持多端登录(不同 Token 对应不同设备),便于踢人下线等功能

5. 方案 5:瑞吉外卖过滤器登录校验(传统 Web)

核心逻辑
  • 技术栈:Servlet Filter + HttpSession + AntPathMatcher

  • 流程:拦截所有请求→ 匹配无需登录路径(如登录 / 退出接口)→ 放行无需登录请求→ 校验 Session 中用户存在性→ 未登录则返回 NOTLOGIN,前端跳转登录页

  • 关键设计:

    • 路径匹配:用 AntPathMatcher 支持通配符(如/backend/**),灵活配置放行路径

    • 响应方式:通过输出流返回 JSON 结果,前端根据NOTLOGIN状态跳转

    • 组件扫描:用@ServletComponentScan自动注册过滤器

核心亮点
  • 适配传统 Web:专注页面访问权限控制,未登录直接跳转登录页

  • 配置简单:无需复杂框架,依赖 Servlet 原生 Filter,学习成本低

  • 轻量高效:拦截器链执行早,性能损耗小,适合中小规模传统 Web 应用

二、关键维度横向对比

对比维度方案 1:JWT 双 Token方案 2:通用登录模板方案 3:基于 Session方案 4:Redis 替代 Session方案 5:过滤器校验(瑞吉)
状态类型无状态可无状态 / 有状态有状态无状态(Token+Redis)有状态(Session)
核心技术JWT + BCrypt泛型抽象 + JWTHttpSession + ThreadLocalRedis + UUID + 拦截器Servlet Filter + Session
适用架构前后端分离 / 微服务多系统复用场景单体传统 Web / 前后端分离分布式单体 / 微服务传统 Web 应用(页面访问)
分布式支持天然支持支持(依赖 JWT/Redis)需 Session 共享(复杂)天然支持需 Session 共享(复杂)
安全特性双 Token + 签名 + BCrypt继承 JWT / 加密特性依赖容器安全过期自动清理 + 加密存储路径拦截 + Session 隔离
扩展性高(支持多端 / 刷新)极高(泛型 + 钩子)低(Session 局限)高(Redis 灵活操作)中(路径配置灵活)
实现复杂度中(需理解 JWT)中(需设计抽象)低(原生 API)中(需 Redis 操作)低(Filter 简单配置)
核心优势无状态 + 无感刷新高复用 + 标准化简单直接分布式友好 + 性能优轻量 + 页面权限控制
核心劣势Token 无法主动失效学习成本略高分布式部署麻烦依赖 Redis 中间件仅适配传统 Web

三、深度讲解:核心差异与选型逻辑

1. 无状态 vs 有状态:架构选型的核心

  • 无状态方案(JWT 双 Token、Redis 替代 Session):

    • 核心思想:服务器不存储登录状态,身份信息通过 Token/Redis 键值对携带

    • 适合场景:微服务、前后端分离、高并发分布式架构(如电商、IM)

    • 关键解决:避免 Session 共享的复杂性(如 Redis Session 同步、Tomcat 集群 Session 拷贝)

  • 有状态方案(Session、过滤器校验):

    • 核心思想:服务器存储用户状态(Session),通过 JSESSIONID 关联用户

    • 适合场景:单体传统 Web 应用(如管理后台、内部系统)

    • 关键优势:实现简单,无需额外中间件,开发成本低

2. 密码加密:安全性的基础

  • 方案 1/2/4:用 BCrypt 加密(推荐)

    • 特点:不可逆,自带盐值,相同明文加密结果不同,防彩虹表攻击

    • 适配场景:生产环境,对安全性要求高的系统

  • 方案 3(部分):用 MD5 加密(不推荐)

    • 特点:不可逆但可通过彩虹表破解,需手动加盐,安全性低于 BCrypt

    • 适配场景: legacy 系统兼容,新系统不建议使用

3. Token vs Session:状态存储的选择

  • Token(JWT):

    • 优点:无状态、分布式友好、无需服务器存储

    • 缺点:无法主动失效(需维护黑名单)、过期时间难平衡(过长不安全,过短影响体验)

  • Session:

    • 优点:可主动失效(invalidate)、自带过期管理、开发简单

    • 缺点:分布式部署需同步、内存占用随用户量增长而增加

  • Redis 替代 Session:

    • 结合两者优势:分布式友好(Redis 共享)+ 可主动失效(删除 Key)+ 性能优

4. 拦截器 vs 过滤器:权限校验的执行时机

  • 过滤器(Filter):

    • 执行时机:请求进入 Servlet 容器后,Controller 前

    • 适用场景:全局请求拦截(如所有路径的登录校验、字符编码过滤)

    • 特点:依赖 Servlet API,不依赖 Spring,执行早,性能损耗小

  • 拦截器(HandlerInterceptor):

    • 执行时机:Spring MVC 的 DispatcherServlet 之后,Controller 方法前

    • 适用场景:Spring 生态内的权限校验(如登录状态、接口权限)

    • 特点:可访问 Spring 容器中的 Bean,支持更细粒度的控制(如方法级拦截)

四、选型建议

  1. 若为前后端分离 + 微服务 / 分布式架构:优先选「方案 1(JWT 双 Token)」,无状态适配分布式,无感刷新提升用户体验;若需多系统复用,叠加「方案 2(通用登录模板)」。

  2. 若为单体传统 Web 应用(如管理后台):优先选「方案 3(基于 Session)」或「方案 5(过滤器校验)」,实现简单,无需额外中间件。

  3. 若为单体但需支持分布式部署:优先选「方案 4(Redis 替代 Session)」,解决 Session 共享问题,兼顾性能与简单性。

  4. 若为多系统开发(如公司内部多个产品):优先选「方案 2(通用登录模板)」,统一登录流程,减少重复开发,降低维护成本。

  5. 安全性要求高(如金融、电商):选「方案 1 + 方案 2」,双 Token+BCrypt 加密 + 通用模板标准化,兼顾安全与复用。

五、总结

所有登录方案的核心目标都是「安全验证身份 + 维护登录状态 + 保障访问权限」,差异仅在于技术栈适配和架构兼容:

  • 简单场景选有状态(Session / 过滤器),复杂架构选无状态(JWT/Redis);

  • 单系统选专用方案,多系统选通用模板;

  • 传统 Web 重页面跳转,前后端分离重 Token 交互。

选择时无需追求 “最先进”,只需匹配自身项目的架构规模、开发成本和安全需求,即可实现高效可靠的登录功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值