我正在使用 json
编写 rest
服务。对于后端,我使用 Spring Security
。我有女巫用 ajax rest 对象发送的表单,如下所示:
{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();
}
}
问题是 spring 安全要求我将凭据作为正文发送,但我想 spring 接受我的 Json 对象。
所以我基于 this 编写了自己的身份验证过滤器:
@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 post 请求时,我得到 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
不存在,因为我有自己的登录名形式。
请帮忙。
编辑
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你必须使用 Post,你将永远无法使用 GET 发布请求正文
关于java - Spring security-在 rest 服务中将凭据作为 json 而不是常规形式发送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35724278/