Java Spring安全性-在rest服务中以json而不是常规形式发送凭据
我正在用Java Spring安全性-在rest服务中以json而不是常规形式发送凭据,java,json,spring,web-services,rest,Java,Json,Spring,Web Services,Rest,我正在用json编写rest服务。对于后端,我使用Spring-Security。我使用ajax rest对象发送表单,如下所示: {email: "admin", password: "secret"} 现在在服务器上,我有如下配置: @Configuration @EnableWebSecurity @ComponentScan("pl.korbeldaniel.cms.server") public class SecurityConfig extends WebSecurityConf
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对象
因此,我基于以下内容编写了自己的身份验证过滤器:
但不幸的是,这个过滤器似乎不起作用
当我从登录表单发送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
,该错误不存在,因为我有自己的登录表单
请帮忙
编辑
公共类WebServletInitializer扩展了AbstractAnnotationConfigDispatchersServletInitializer{
@凌驾
受保护类[]getRootConfigClasses(){
返回新类[]{SecurityConfig.Class};
}
@凌驾
受保护类[]getServletConfigClasses(){
返回新类[]{WebConfig.Class};
}
@凌驾
受保护的字符串[]getServletMappings(){
//返回新字符串[]{”/“};
//返回新字符串[]{“/cms/”};
返回新字符串[]{“/services/*”};
}
}
@EnableWebMvc
@组件扫描(basePackages=“pl.daniel.cms.server”)
公共类WebConfig扩展了WebMVCConfigureAdapter{
}
好吧,在您编写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());
}
p.D.您必须使用Post,您将永远无法使用GET发布请求正文。您可以扩展和覆盖
WebSecurityConfigureAdapter
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilter(new UserNamePasswordAuthFilter(authenticationManager(), userRepo))
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService); // custom user service
provider.setPasswordEncoder(passwordEncoder); // custom password encoder
return provider;
}
然后,您可以为身份验证定义一个过滤器,还可以选择覆盖成功的登录行为
public class UserNamePasswordAuthFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authManager;
private final AecUserRepo userRepo;
public UserNamePasswordAuthFilter(AuthenticationManager authManager, AecUserRepo userRepo) {
super();
this.authManager = authManager;
this.userRepo = userRepo;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
try {
// Get username & password from request (JSON) any way you like
UsernamePassword authRequest = new ObjectMapper()
.readValue(request.getInputStream(), UsernamePassword.class);
Authentication auth = new UsernamePasswordAuthenticationToken(authRequest.getUsername(),
authRequest.getPassword());
return authManager.authenticate(auth);
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
// custom code
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
这完全取决于你想做什么。此身份验证请求是否由用户通过AJAX提交?想要的输出是否是经过身份验证的会话(基于cookie)?用户使用客户机web app(js)表单为concret url发送ajax POST请求。我更喜欢token而不是cookie,但cookie可以做到这一点。那个么为什么你们坚持使用JSON编码的正文呢?只需按照服务器期望的方式对AJAX登录请求进行编码就不那么简单了。对于客户端,我使用GWT和RestyGWT进行通信。我不能弄乱那些库。我应该在哪里调用
getUserNamePasswordAuthenticationToken()
方法?我已经在UsernamePasswordAuthenticationToken authRequest=this.getUserNamePasswordAuthenticationToken(请求)中被调用了代码>来自FilterStore中的尝试身份验证方法。糟糕,我已经重写了过滤类。我已经按照你的指示做了,但它不起作用,这方面的任何代码都不会执行。你记得在web.xml中设置spring安全过滤器链吗?我使用java配置。我想是的。我将用java配置更新帖子。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilter(new UserNamePasswordAuthFilter(authenticationManager(), userRepo))
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService); // custom user service
provider.setPasswordEncoder(passwordEncoder); // custom password encoder
return provider;
}
public class UserNamePasswordAuthFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authManager;
private final AecUserRepo userRepo;
public UserNamePasswordAuthFilter(AuthenticationManager authManager, AecUserRepo userRepo) {
super();
this.authManager = authManager;
this.userRepo = userRepo;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
try {
// Get username & password from request (JSON) any way you like
UsernamePassword authRequest = new ObjectMapper()
.readValue(request.getInputStream(), UsernamePassword.class);
Authentication auth = new UsernamePasswordAuthenticationToken(authRequest.getUsername(),
authRequest.getPassword());
return authManager.authenticate(auth);
} catch (Exception exp) {
throw new RuntimeException(exp);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
// custom code
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}