security 登录改为 post json 登录 多次获取body 问题
原因: security filter 和 自定义 filter 执行顺序问题,框架会优先执行 17个 security filter ,再执行 自定义的 filter
,所以直接多次获取body 是不行的,需要把自定义的 filter 放到 security filter 中
重写登录过滤器
重写 UsernamePasswordAuthenticationFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import com.stark37125.core.util.RequestUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@Slf4j public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public JsonUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); }
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!RequestUtils.isPost(request)) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); }
if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { UsernamePasswordAuthenticationToken authRequest; LoginUserParam loginUserParam = LoginUserParamHolder.LOGIN_USER_PARAM_LOCAL.get(); authRequest = new UsernamePasswordAuthenticationToken(loginUserParam.getUsername(), loginUserParam.getPassword()); setDetails(request, authRequest); LoginUserParamHolder.LOGIN_USER_PARAM_LOCAL.remove(); return this.getAuthenticationManager().authenticate(authRequest); } else { return super.attemptAuthentication(request, response); } }
}
|
WebSecurityConfig
配置
1 2 3 4
| .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|
JsonUsernamePasswordAuthenticationFilter
1 2 3 4 5 6 7 8
| @Bean public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() throws Exception { JsonUsernamePasswordAuthenticationFilter filter = new JsonUsernamePasswordAuthenticationFilter(authenticationConfiguration.getAuthenticationManager()); filter.setAuthenticationSuccessHandler(successHandler); filter.setAuthenticationFailureHandler(failHandler); filter.setFilterProcessesUrl("/system/login"); return filter; }
|
方法一 使用 ThreadLocal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import com.stark37125.core.util.RequestUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@Slf4j public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public JsonUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); }
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!RequestUtils.isPost(request)) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); }
if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { UsernamePasswordAuthenticationToken authRequest; LoginUserParam loginUserParam = LoginUserParamHolder.LOGIN_USER_PARAM_LOCAL.get(); authRequest = new UsernamePasswordAuthenticationToken(loginUserParam.getUsername(), loginUserParam.getPassword()); setDetails(request, authRequest); LoginUserParamHolder.LOGIN_USER_PARAM_LOCAL.remove(); return this.getAuthenticationManager().authenticate(authRequest); } else { return super.attemptAuthentication(request, response); } }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| import com.alibaba.fastjson2.JSON; import com.anji.captcha.model.common.ResponseModel; import com.anji.captcha.model.vo.CaptchaVO; import com.anji.captcha.service.CaptchaService; import com.stark37125.core.base.exception.LoginFailException; import com.stark37125.core.config.security.LoginUserParam; import com.stark37125.core.config.security.LoginUserParamHolder; import com.stark37125.core.util.HttpHelper; import com.stark37125.core.util.RequestUtils; import com.stark37125.core.util.SpringContextHolder; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.http.MediaType; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.web.bind.ServletRequestUtils; import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@Component public class ValidateCodeFilter extends OncePerRequestFilter {
private static final String CAPTCHA_VERIFICATION = "captchaVerification";
@Override protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); if (StringUtils.equals("/system/login", request.getRequestURI()) && RequestUtils.isPost(request)) { String captchaVerification = getCaptchaVerification(request); if (StringUtils.isBlank(captchaVerification)) { throw new LoginFailException("验证码参数不能为空"); } CaptchaVO captchaVO = new CaptchaVO(); captchaVO.setCaptchaVerification(captchaVerification); ResponseModel responseModel = captchaService.verification(captchaVO); if (!StringUtils.equals("0000", responseModel.getRepCode())) { throw new LoginFailException(responseModel.getRepMsg()); } } filterChain.doFilter(request, response); }
@SneakyThrows public String getCaptchaVerification(HttpServletRequest request) { if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { String bodyString = HttpHelper.getBodyString(request); if (StringUtils.isBlank(bodyString)) { throw new LoginFailException("参数不能为空"); } LoginUserParam loginUserParam = JSON.parseObject(bodyString, LoginUserParam.class); LoginUserParamHolder.LOGIN_USER_PARAM_LOCAL.set(loginUserParam); return loginUserParam.getCaptchaVerification(); } return ServletRequestUtils.getStringParameter(request, CAPTCHA_VERIFICATION); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import lombok.Data;
@Data public class LoginUserParam {
private String username;
private String password;
private String captchaVerification; }
|
1 2 3 4 5 6 7 8 9 10
|
public class LoginUserParamHolder {
public static final ThreadLocal<LoginUserParam> LOGIN_USER_PARAM_LOCAL = new ThreadLocal<>(); }
|
方法二 WebSecurityConfig
配置 RepeatableFilter
即可在filter 多次获取body
1 2 3 4
| .addFilterBefore(new RepeatableFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) //添加JWT过滤器 除/system/login其它请求都需经过此过滤器 .addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
|