这是我的自定义LoginFilter
:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
public LoginFilter(AuthenticationManager authenticationManager){
super(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// 需要是 POST 请求
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
HttpSession session = request.getSession();
// 获得 session 中的 验证码值
String sessionVerifyCode = (String) session.getAttribute("verify_code");
// 判断请求格式是否是 JSON
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE ) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
Map<String, String> loginData = new HashMap<>();
try {
loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
} catch (IOException e) {
} finally {
String code = loginData.get("code");
checkVerifyCode(sessionVerifyCode, code);
}
String username = loginData.get(getUsernameParameter());
String password = loginData.get(getPasswordParameter());
if (StringUtils.isEmpty(username)) {
throw new AuthenticationServiceException("用户名不能为空");
}
if (StringUtils.isEmpty(password)) {
throw new AuthenticationServiceException("密码不能为空");
}
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
// UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} else {
checkVerifyCode(sessionVerifyCode, request.getParameter("code"));
return super.attemptAuthentication(request, response);
}
}
private void checkVerifyCode(String sessionVerifyCode, String code) {
if (StringUtils.isEmpty(code)) {
throw new AuthenticationServiceException("验证码不能为空!");
}
if (StringUtils.isEmpty(sessionVerifyCode)) {
throw new AuthenticationServiceException("请重新申请验证码!");
}
if (!sessionVerifyCode.equalsIgnoreCase(code)) {
throw new AuthenticationServiceException("验证码错误!");
}
}
}
java
这是spring安全配置:
@Configuration
@EnableConfigurationProperties(IgnoreUrlsConfig.class)
public class SecurityConfig {
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Autowired(required = false)
private DynamicSecurityService dynamicSecurityService;
@Autowired(required = false)
private DynamicSecurityFilter dynamicSecurityFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
.authorizeRequests();
//不需要保护的资源路径允许访问
for (String url : ignoreUrlsConfig.getUrls()) {
registry.antMatchers(url).permitAll();
}
//允许跨域请求的OPTIONS请求
registry.antMatchers(HttpMethod.OPTIONS)
.permitAll();
// 任何请求需要身份认证
registry.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler((req, resp, authentication) -> {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("注销成功");
out.flush();
out.close();
})
// 关闭跨站请求防护及不使用session
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// 自定义权限拒绝处理类
.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.apply(MyCustomDsl.customDsl())
// 自定义权限拦截器JWT过滤器
.and()
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//有动态权限配置时添加动态权限校验过滤器
if(dynamicSecurityService!=null){
registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
}
return httpSecurity.build();
}
}
java
MyCustomDsl.customDsl()
为:
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(loginFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
// http.addFilter(loginFilter(authenticationManager));
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
LoginFilter loginFilter = new LoginFilter(authenticationManager);
loginFilter.setFilterProcessesUrl("/sso/login");
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");
loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
return loginFilter;
}
}
java
主计长是:
@ResponseBody
@RequestMapping(value = "/sso/login", method = RequestMethod.POST)
public CommonResult login(@RequestBody LoginParam loginParam){
String token = umsAdminService.login(loginParam.getUsername(), loginParam.getPassword());
if(token == null){
return CommonResult.validateFailed("用户名或密码有误");
}
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
return CommonResult.success(tokenMap);
}
java
我期望它执行Controller来生成令牌并返回它,但是 Postman 测试的结果是打印回调函数以表示我的身份验证成功。
这是我的验证成功处理程序:
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Object principal = authentication.getPrincipal();
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
out.write(mapper.writeValueAsString(principal));
out.flush();
out.close();
}
}
java
当我postman测试接口/sso/login时,打印的结果是我上面的成功回调函数。
1条答案
按热度按时间ocebsuys1#
我重新整理了一下逻辑,终于达到了我的预期,逻辑是:前端提交一个接口/sso/login,该接口没有经过鉴权,接口接受前端参数(用户名和密码)来生成返回到前端的令牌。我为令牌身份验证定义了JwtAuthenticationTokenFilter过滤器,前端接口尚未通过身份验证,因此无法让它转到Controller的接口/sso/login进行身份验证。接下来我给予使用LoginFilter这个过滤器,而是使用继承DaoAuthenticationProvider这个类来进行验证码的认证。