我正在rest与一起写服务json。对于后端,我使用Spring Security。我有形式巫婆发送与ajax rest对象,如下所示:
rest
json
Spring Security
{email: "admin", password: "secret"}
现在在服务器上,我的配置如下:
@Configuration @EnableWebSecurity @ComponentScan("pl.korbeldaniel.cms.server") public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private RestAuthenticationSuccessHandler authenticationSuccessHandler; @Autowired private RestAuthenticationFailureHandler authenticationFailureHandler; @Bean JsonAuthenticationFilter jsonAuthenticationFilter() throws Exception { JsonAuthenticationFilter filter = new JsonAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); System.out.println("jsonAuthenticationFilter"); return filter; } @Bean public RestAuthenticationSuccessHandler mySuccessHandler() { return new RestAuthenticationSuccessHandler(); } @Override @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("admin").password("secret").roles("ADMIN"); // auth.jdbcAuthentication(). } @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); http.csrf().disable();// http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)// .and().authorizeRequests()// .antMatchers("/").permitAll()// .antMatchers("/services/anonymous/**").permitAll()// .antMatchers("/services/authenticated/**").authenticated()// .and().formLogin().loginProcessingUrl("/services/anonymous/loginService/login").usernameParameter("email").passwordParameter("password")// .successHandler(authenticationSuccessHandler)// .and().logout().logoutUrl("/services/anonymous/loginService/logout"); // http.httpBasic(); } }
问题是春季安全要求我发送凭据作为正文,但是我想春季接受我的Json对象。
因此,我基于此编写了自己的身份验证过滤器:
@Component public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private boolean postOnly; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { System.out.println("attemptAuthentication"); if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } UsernamePasswordAuthenticationToken authRequest = this.getUserNamePasswordAuthenticationToken(request); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * @param request * @return */ private UsernamePasswordAuthenticationToken getUserNamePasswordAuthenticationToken(HttpServletRequest request) { // TODO Auto-generated method stub System.out.println(request); return null; } }
但是不幸的是,这个过滤器似乎不起作用。
当我从登录表单发送ajax发布请求时,我得到了302 Found,然后得到了:
302 Found
Remote Address:127.0.0.1:8080 Request URL:http://localhost:8080/cms/login?error Request Method:GET Status Code:404 Not Found
就像那里没有验证用户凭据(因为表单主体为空,并且凭据作为json)一样,然后将其重定向到login?error不存在的原因,因为我有自己的登录表单。
login?error
请帮忙。
编辑
public class WebServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { SecurityConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { // return new String[] { "/" }; // return new String[] { "/cms/" }; return new String[] { "/services/*" }; } } @EnableWebMvc @ComponentScan(basePackages = "pl.daniel.cms.server") public class WebConfig extends WebMvcConfigurerAdapter { }
好吧,除非您编写 getUserNamePasswordAuthenticationToken 主体,否则它一定不能起作用。
实际上,您必须阅读HttpServletRequest的请求正文,通过Jackson或其他任何映射方式对其进行解析,并使用它创建UsernamePasswordAuthenticationToken。
使用Jackson(根据您的Spring版本选择正确的版本),我将创建一个像这样的简单bean:
@JsonIgnoreProperties(ignoreUnkown=true) public LoginRequest{ private String email; private String password; // getters & setters }
使用它来映射它的请求主体:
private UsernamePasswordAuthenticationToken getUserNamePasswordAuthenticationToken(HttpServletRequest request) throws IOException{ StringBuffer sb = new StringBuffer(); BufferedReader bufferedReader = null; String content = ""; LoginRequest sr = null; try { bufferedReader = request.getReader() char[] charBuffer = new char[128]; int bytesRead; while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 ) { sb.append(charBuffer, 0, bytesRead); } content = sb.toString(); ObjectMapper objectMapper = new ObjectMapper(); try{ sr = objectMapper.readValue(content, LoginRequest.class); }catch(Throwable t){ throw new IOException(t.getMessage(), t); } } catch (IOException ex) { throw ex; } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException ex) { throw ex; } } } return new UsernamePasswordAuthenticationToken(sr.getEmail(), sr.getPassword());
}
PD Yo必须使用Post,否则您将永远无法使用GET发布请求正文