You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Android应用REST API用户认证方案选型及过滤器查库合理性探讨

三种用户数据库认证方案的对比与最佳实践

嗨,我来帮你梳理这三种用户认证方案的优劣,以及你关心的Filter中认证是否为最佳实践的问题——先给你个明确结论:Spring Security是绝大多数场景下的理想方案,另外两种方案要么耦合性太高,要么需要自己处理大量繁琐的安全细节。

1. 在Controller类中查询数据库认证

这种方案是最不推荐的,问题非常明显:

  • 业务逻辑和认证逻辑完全耦合,每个需要认证的接口都要重复写查询用户、校验权限的代码,冗余度拉满。
  • 无法统一处理认证失败的场景(比如每个Controller都要手动返回401),完全违背了单一职责原则。

举个反例,你可能会在每个接口开头写:

@GetMapping("/user/profile")
public ResponseEntity<UserProfile> getProfile(@RequestHeader("Authorization") String token) {
    User user = userRepository.findByToken(token);
    if (user == null) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
    // 业务逻辑...
}

这种写法后期维护起来简直是噩梦。

2. 在Filter类中查询数据库认证

这种方案比Controller里写要好,能把认证逻辑抽离出来统一拦截请求,但绝对不是最佳实践,主要问题有这些:

  • 你提供的代码里直接在Filter里创建Connection,这会导致数据库连接泄漏、线程安全问题,而且完全没利用Spring的依赖注入(比如不能直接用UserRepository)。
  • 需要自己手动处理认证结果的上下文传递(比如把用户信息放到Request属性里,后续Controller还要手动取),没有Spring Security自带的SecurityContext方便。
  • 缺少Spring Security内置的权限校验、会话管理、CSRF防护等安全特性,你得自己实现这些,很容易踩安全坑。

如果实在要使用Filter,也得规范写法,比如利用Spring的Bean注入来管理数据库操作,改进后的示例代码如下:

@Component
public class TokenValidationFilter implements Filter {

    private final UserRepository userRepository;

    // 用构造器注入Spring管理的Repository,避免手动管理连接
    public TokenValidationFilter(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        final String accessToken = req.getHeader("Authorization");
        if (accessToken == null || !accessToken.startsWith("Bearer ")) {
            res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or missing token");
            return;
        }

        String token = accessToken.substring(7);
        User user = userRepository.findByToken(token); // 用Spring的Repository查库
        if (user == null) {
            res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token");
            return;
        }

        // 将用户信息放入请求上下文,方便后续Controller使用
        req.setAttribute("currentUser", user);
        chain.doFilter(req, res);
    }
}

3. 使用Spring Security(强烈推荐)

这是Spring生态下做认证的标准方案,它已经帮你封装了所有安全细节,你只需要专注于用户信息的获取逻辑,优势非常明显:

  • 完全遵循Spring的设计原则,依赖注入、AOP等特性都能用上,代码优雅易维护。
  • 自带各种安全防护(CSRF、XSS、会话固定攻击防护等),不用自己造轮子踩坑。
  • 扩展性极强,后续要加OAuth2、LDAP认证只需要简单配置即可。

核心代码示例

1. 自定义UserDetailsService(从数据库查询用户)

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String token) throws UsernameNotFoundException {
        // 这里根据token查询用户,你也可以根据用户名,取决于你的认证逻辑
        User user = userRepository.findByToken(token);
        if (user == null) {
            throw new UsernameNotFoundException("User not found with token: " + token);
        }
        // 封装成Spring Security的UserDetails对象,包含权限信息
        return org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword()) // 如果是JWT认证,这里可以忽略,或者存加密后的密码
                .authorities(user.getRoles().stream().map(SimpleGrantedAuthority::new).toList())
                .build();
    }
}

2. Spring Security配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomUserDetailsService userDetailsService;

    public SecurityConfig(CustomUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter(authenticationManagerBean());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // REST API通常禁用CSRF
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/public/**").permitAll() // 公开接口无需认证
                        .anyRequest().authenticated() // 其他接口必须认证
                )
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

3. JWT认证过滤器(处理token校验)

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = extractTokenFromRequest(request);
        if (token != null && validateToken(token)) {
            // 创建认证请求,交给AuthenticationManager处理
            Authentication authentication = new UsernamePasswordAuthenticationToken(token, null, Collections.emptyList());
            Authentication authenticated = authenticationManager.authenticate(authentication);

            // 将认证信息放入SecurityContext,后续可通过SecurityContextHolder获取
            SecurityContextHolder.getContext().setAuthentication(authenticated);
        }
        filterChain.doFilter(request, response);
    }

    private String extractTokenFromRequest(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }

    private boolean validateToken(String token) {
        // 这里实现JWT的校验逻辑,比如签名、过期时间等
        try {
            Jwts.parser().setSigningKey("your-secret-key").parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

总结

如果你是开发生产环境的API,优先选择Spring Security,它能帮你避免很多安全漏洞,代码也更易维护。Filter方案只适合一些简单的小项目,或者需要高度定制认证逻辑的场景,但也要规范写法,利用Spring的依赖注入,不要手动管理数据库连接。

内容的提问来源于stack exchange,提问作者Ashish Rathee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:54:29