Java Spring Security客户端加密服务端解密后密码无法传递至后续过滤器问题
问题分析与解决方案
核心问题
你当前的代码存在两个关键问题导致解密后的密码无法传递:
- Filter重复添加:
WebSecurityConfig里同时调用了addFilterBefore(passwordDecryptionFilter, UsernamePasswordAuthenticationFilter.class)和addFilter(passwordDecryptionFilter),会导致Filter被执行两次,可能覆盖请求参数的处理逻辑。 - Filter实现逻辑错误:你提供的代码是
AuthenticationProvider的authenticate方法,而非真正的请求拦截Filter——解密逻辑应该拦截登录请求、修改请求中的密码参数,而非直接处理Authentication对象。
正确实现步骤
1. 编写正确的密码解密Filter
实现OncePerRequestFilter,拦截登录请求并通过包装请求修改密码参数:
public class PasswordDecryptionFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 仅拦截登录POST请求 if ("/login".equals(request.getServletPath()) && "POST".equalsIgnoreCase(request.getMethod())) { // 包装请求以支持修改参数 HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) { @Override public String getParameter(String name) { String value = super.getParameter(name); // 对密码参数执行解密 if ("password".equals(name) && value != null) { try { return decryptPassword(value); // 替换为你的实际解密逻辑 } catch (Exception e) { throw new RuntimeException("密码解密失败", e); } } return value; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> map = new HashMap<>(super.getParameterMap()); String[] passwords = map.get("password"); if (passwords != null && passwords.length > 0) { try { passwords[0] = decryptPassword(passwords[0]); map.put("password", passwords); } catch (Exception e) { throw new RuntimeException("密码解密失败", e); } } return map; } }; filterChain.doFilter(wrappedRequest, response); } else { filterChain.doFilter(request, response); } } // 自定义解密方法,替换为实际加密算法对应的逻辑 private String decryptPassword(String encryptedPassword) throws Exception { // 示例:此处编写AES/RSA等解密代码 return encryptedPassword; } }
2. 修正WebSecurityConfig配置
移除重复的Filter添加代码,确保解密Filter在UsernamePasswordAuthenticationFilter之前执行:
@Override protected void configure(HttpSecurity http) throws Exception { // 仅添加一次解密Filter,放在UsernamePasswordAuthenticationFilter之前 http.addFilterBefore(passwordDecryptionFilter, UsernamePasswordAuthenticationFilter.class); http.csrf().csrfTokenRepository(csrfTokenRepository()); http.authorizeRequests() .antMatchers("/login*", "/css/**", "/js/**", "/pages/**", "/img/**", "/fonts/**", "/plugins/**", "/jrxml/**").permitAll() .antMatchers("/tokenValidation").hasAuthority(Authorities.TOTP_AUTH_AUTHORITY) .anyRequest().authenticated(); Cookie sameSiteCookie = new Cookie("SameSite", "None"); http.addFilterBefore(new SamSiteAndCSPFilter(sameSiteCookie), UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(totpAuthFilter, UsernamePasswordAuthenticationFilter.class); http.exceptionHandling().accessDeniedPage("/403"); http.formLogin() .loginPage("/login") .usernameParameter("username") .passwordParameter("password") .failureUrl("/login?error=true") .failureHandler(customAuthenticationFailureHandler) .successHandler(customAuthenticationSuccessHandler); http.sessionManagement() .sessionFixation().migrateSession() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .invalidSessionUrl("/login") .maximumSessions(1) .sessionRegistry(sessionRegistry()) .and() .sessionFixation().none(); http.logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .invalidateHttpSession(true) .clearAuthentication(true) .logoutSuccessUrl("/raya/login?logSucc=true") .logoutSuccessHandler(logoutSuccessHandler()) .deleteCookies("JSESSIONID") .permitAll(); }
3. 验证AuthenticationProvider逻辑
确保从Authentication对象中获取的是解密后的密码:
@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String decryptedPassword = authentication.getCredentials().toString(); System.out.println("解密后的密码: " + decryptedPassword); User user = userRepository.findUserByUserName(username); if (user == null) { throw new BadCredentialsException("用户名不存在"); } // 用密码编码器验证解密后的密码与数据库存储的加密密码是否匹配 if (!encoder().matches(decryptedPassword, user.getPassword())) { throw new BadCredentialsException("密码错误"); } return new UsernamePasswordAuthenticationToken(user, null, getAuthorities(Arrays.asList(user.getRole()))); }
关键注意事项
- 禁止重复添加同一个Filter,避免请求被多次处理导致参数异常。
- 必须通过
HttpServletRequestWrapper包装请求,原生请求参数不可直接修改。 - 确保客户端加密逻辑与服务端解密逻辑完全匹配,防止解密失败。
内容的提问来源于stack exchange,提问作者Greko2015 GuFn
相关产品推荐
相关产品推荐

