java - 用于休息应用程序的 Spring Security。 POST 总是返回 403 代码

标签 java spring spring-mvc spring-security spring-restcontroller

这是我的 spring-security.xml:

    <security:http pattern="/eklienci/**"
        authentication-manager-ref="authenticationManager" entry-point-ref="restAuthenticationEntryPoint"
        create-session="stateless">
        <security:intercept-url pattern="/eklienci/**"
            access="hasAnyAuthority('ADMIN','USER','VIEWER')" />
        <form-login
            authentication-success-handler-ref="mySuccessHandler"
            authentication-failure-handler-ref="myFailureHandler"
        />
        <security:custom-filter ref="restServicesFilter"
            before="PRE_AUTH_FILTER" />
    </security:http>
    <!-- other stuff --!>
  <beans:bean id="restAuthenticationEntryPoint"
      class="pl.aemon.smom.config.RestAuthenticationEntryPoint" />
  <!-- Filter for REST services. -->
  <beans:bean id="restServicesFilter"
    class="pl.aemon.smom.config.RestUsernamePasswordAuthenticationFilter">
    <beans:property name="postOnly" value="true" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationSuccessHandler" ref="mySuccessHandler" />
 </beans:bean>

这是我的 RestUsernamePasswordAuthenticationFilter:

public class RestUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{

    @Autowired
    private CustomAuthenticationProvider authenticationProvider;


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        Enumeration<String> names = request.getHeaderNames();
        while(names.hasMoreElements())
        {
            System.out.println(names.nextElement());
        }
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        System.out.println("Username " + username + " password: " + password);
        if(username!=null)
        {
            username = username.trim();
        }

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return authenticationProvider.authenticateRest(authRequest);

//        System.out.println(auth.toString());
//        return auth;
    }



    @Override
    protected String obtainPassword(HttpServletRequest request) {
        return request.getHeader("password");
    }

    @Override
    protected String obtainUsername(HttpServletRequest request) {
        return request.getHeader("username");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        Authentication auth = attemptAuthentication(httpRequest, httpResponse);
        SecurityContextHolder.getContext().setAuthentication(auth);

        chain.doFilter(request, response);

    }
}

这是我的 RestController 方法

@RestController
@RequestMapping("/eklienci")
public class EklientRestController
{
    @RequestMapping(value="/get/{eklientid}")
    public Eklient get(@PathVariable String eklientid) 
    {
        return userService.findById(eklientid);
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST, produces="application/json", consumes="application/json")
    @ResponseBody
    public String add(@RequestBody String json) 
    {
        System.out.println(json);
        Eklient pj = new Eklient();
        ObjectMapper mapper = new ObjectMapper();
        try
        {
            pj = mapper.readValue(json, Eklient.class);
            return mapper.writeValueAsString(pj);
        } catch (JsonParseException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e)
        {
        // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return "Error";
    }
}

当我尝试调用/get/{eklientid} 时,它总是工作正常。所有 GET 调用始终至少返回有关 UNARTHORIZED 访问 (401) 的信息,我看到来自 RestUsernamePasswordAuthenticationFilter 的日志。

但是当我尝试任何 POST 调用时(例如/eklienci/add},我的应用程序总是返回 403 代码并且不生成任何日志。原因是什么?如何解决?

最佳答案

CSRF对于常见的非 GET 方法,如 POST、PUT、DELETE,如果您没有在 REST 调用中放置 CSRF header ,则默认情况下会导致 403。您可以(暂时!)关闭 security.xml 中的 CSRF 以确认问题所在。主要是你需要添加 CSRF headers进入您的 REST 调用和 <sec:csrfInput/> 任何客户端-服务器调用的 JSP 标记。仅供引用,我需要在我的开源项目中实现的其他类,可能对您有用:

  1. CsrfSecurityRequestMatcher为某些不需要授权的 POST/PUT 等关闭 CSRF。按照配置here在我的 security.xml 中。
  2. CustomAccessDeniedHandlerImpl将 session 超时导致的 CSRF 403 路由到登录页面。

关于java - 用于休息应用程序的 Spring Security。 POST 总是返回 403 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37907105/

相关文章:

java - 如何使用整数数组进行间距计数来形成垂直字符串图案?

java - 为什么空闲 Java 线程显示高 CPU 使用率?

java - SpringBoot 使用 MongoDB 实现通用 DAO

java - 从 war 中外部化应用程序上下文和 hibernate.properties

java - bean 定义未加载到 spring 上下文中

spring - Spring Security不会发布到提供的登录处理URL

java - 无法从 spring mvc 中的属性文件读取

java - 寻找在 Spring MVC 中显示计数的更好方法

java - 自纪元以来的时间和日期

java - 使用 HTTPBuilder 并获取 "java.net.SocketException: No buffer space available (maximum connections reached?): connect"