Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring安全性-在rest服务中以json而不是常规形式发送凭据_Java_Json_Spring_Web Services_Rest - Fatal编程技术网

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);       
    }
}