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

多身份登录场景是否需要单独编写JWT Filter处理客户登录校验?

不需要额外编写新的JWT Filter,直接复用现有逻辑即可,以下是3种可选实现方案:

方案1:JWT载荷添加身份标识(最推荐,复杂度最低)

改造步骤:

  • 先修改登录生成JWT的逻辑:普通用户和客户的登录接口分开处理,生成token时在JWT载荷中新增user_type字段,取值为user/customer区分身份类型。
  • 扩展JwtTokenUtil工具类,新增getUserTypeFromToken(String token)方法,支持从JWT中解析出user_type字段。
  • 调整现有JwtRequestFilter代码,注入两个UserDetailsService实现,根据身份标识选择对应服务加载用户:
@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    // 原有普通用户UserDetailsService
    @Autowired
    private UserAccountService userAccountService;
    
    // 新增客户侧UserDetailsService
    @Autowired
    private CustomerUserDetailsService customerUserDetailsService;
    
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String requestTokenHeader = request.getHeader("Authorization");
        String username = null;
        String jwtToken = null;
        String userType = null;
        
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
                // 新增解析身份类型逻辑
                userType = jwtTokenUtil.getUserTypeFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } 

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 根据身份类型选择对应UserDetailsService
            UserDetails userDetails = null;
            if ("user".equals(userType)) {
                userDetails = userAccountService.loadUserByUsername(username);
            } else if ("customer".equals(userType)) {
                userDetails = customerUserDetailsService.loadUserByUsername(username);
            }
            
            if (userDetails != null && jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        chain.doFilter(request, response);
    }
}

注意:该方案需要保证普通用户和客户的username全局唯一,如果无法保证唯一,可以将用户ID、身份标识同时存入JWT,用ID+身份类型联合查询避免冲突。


方案2:策略模式封装多身份查询(适合后续会扩展更多身份类型的场景)

如果后续还要新增管理员、服务商等其他身份类型,可以用策略模式避免Filter代码反复修改:

  • 定义通用策略接口:
public interface UserDetailsServiceStrategy {
    // 判断当前实现是否支持对应身份类型
    boolean support(String userType);
    UserDetails loadUserByUsername(String username);
}
  • 让UserAccountService、CustomerUserDetailsService都实现该接口,分别在support方法中返回对应支持的user_type取值。
  • 编写工厂类注入所有策略实现:
@Component
public class UserDetailsServiceFactory {
    @Autowired
    private List<UserDetailsServiceStrategy> strategyList;
    
    public UserDetailsService getService(String userType) {
        return strategyList.stream()
                .filter(strategy -> strategy.support(userType))
                .findFirst()
                .orElseThrow(() -> new RuntimeException("不支持的身份类型"));
    }
}
  • Filter中直接注入工厂类即可,后续新增身份不需要修改Filter代码,符合开闭原则。

方案3:合并查询逻辑(适合小体量简单场景)

如果不想修改JWT生成逻辑,可以写一个合并的UserDetailsService,先查普通用户表,查不到再查客户表,缺点是如果两边有重名用户会出现冲突,性能也相对较低。


额外注意事项

  • 客户对应的自定义UserDetails实现类需要正确实现getAuthorities()方法,为客户分配独立的权限标识,方便后续接口鉴权区分普通用户和客户。
  • 登录入口建议分开,比如/api/user/login和/api/customer/login,各自处理对应身份的账号密码校验、JWT生成逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 17:27:03