为什么有些URL被“禁止”而有些不在我的Web应用程序中

时间:2019-05-07 20:46:41

标签: jquery-mobile spring-security spring-web

我的Web应用程序遇到奇怪的行为。当您尝试启动该应用程序时,它会要求您按预期方式登录,并带您进入欢迎页面(/),然后可以选择个人资料(/profile)页面或搜索页(/search)。如果您尝试不登录而访问任何这些页面,它将按预期将您重定向到登录页面。但是,当您尝试提交搜索条件或提交密码更改时,将返回403 Forbidden。

<security:http use-expressions="true">
    <security:intercept-url pattern="/resources/css/*" access="permitAll"  />
    <security:intercept-url pattern="/resources/images/*" access="permitAll"  />
    <security:intercept-url pattern="/login" access="permitAll"  />
    <security:intercept-url pattern="/logout" access="permitAll"  />
    <security:intercept-url pattern="/accessdenied" access="permitAll"  />
    <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"  />
    <security:form-login 
        login-page="/login" 
        default-target-url="/" 
        authentication-success-handler-ref="loginSuccessHandler" 
        authentication-failure-url="/accessdenied"
    />
    <security:logout 
        logout-success-url="/" 
        logout-url="/perform_logout"
        delete-cookies="JSESSIONID"
    />
</security:http>

网址:

/                       (Welcome Page [GET])
/search                 (Search Page [GET])
/search/data            (Search Query [POST])
/profile                (Profile Page [GET])
/profile/updatePassword (Profile Update [POST])

配置文件控制器

@Controller
@RequestMapping({ "/profile" }) 
public class ProfileController {
    @Autowired
    UserService userService = null;
    @Autowired
    ProfileService profileService = null;

    @RequestMapping(value = { "/", "" }, method = RequestMethod.GET)
    public String getProfile(Model model) {
        Profile profile = profileService.getProfile();
        model.addAttribute("profile", profile);
        return "profile";
    }

    @RequestMapping(value = { "/updatePassword" }, method = RequestMethod.POST)
    public @ResponseBody AjaxResponse updatePassword(@RequestBody Profile profile) {
        // do stuff
        return new AjaxResponse(response, null, errors);
    }
}

搜索控制器

@Controller
@RequestMapping({ "/search" }) 
public class StockKeepingUnitController {
    @Autowired(required = true)
    private SkuService skuService;
    @Autowired(required = true)
    private UserService userService;

    @RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
    public String search() {
        return "search";
    }

    @RequestMapping(value = "/data", method = RequestMethod.POST)
    public @ResponseBody AjaxResponse data(@RequestBody SearchCriteria searchCriteria) {
        List<StockKeepingUnit> skus = null;
        try {
            String criteria = searchCriteria.getCriteria();
            skus = skuService.listSkusBySearch(criteria);
        } catch (Exception ex) {
            ex.printStackTrace();
            List<String> errors = new ArrayList<>();
            errors.add("Error saving ALOT.");
            return new AjaxResponse("ERROR", null, errors);
        }
        return new AjaxResponse("OK", skus, null);
    }
}

搜索ajax

    $.ajax({url: "${pageContext.request.contextPath}/search/data"
        , method: "POST"
        , contentType: "application/json; charset=utf-8"
        , dataType: "json"
        , data: JSON.stringify(searchCriteria)
        , success: function(ajaxResponse) { /* ... */ }
        , error: function(xhr, status, error) { /* ... */ }
    });

个人资料ajax

$.ajax({
    url: "${pageContext.request.contextPath}/profile/updatePassword",
    , method: "POST"
    , contentType: "application/json; charset=utf-8"
    , dataType: "json"
    , data: JSON.stringify(profile)
    , success : function(ajaxResponse) { /* ... */ }
    , error : function(xhr, status, error) { /* ... */ }
});

---编辑--- jQuery for csrf

$(function() {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

我还发现,如果我重新加载每个页面,则POST提交有效。 CSRF令牌在每页上是否有某种更改方式?我正在使用jQuery Mobile btw。

1 个答案:

答案 0 :(得分:1)

导致此问题的原因是,jQuery Mobile通常不会在每个页面请求(CSRF令牌的存储位置)上加载标头信息。因此,导航到新页面时,它在执行POST时会使用过时的CSRF令牌,这会导致403禁止访问。为了克服这个问题,我通过在页面的每个链接中包含data-ajax="false"来强制JQM进行不带ajax的链接。例如:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<form action="<c:url value="/perform_logout" />" method="POST" name="logoutform">
    <input type="hidden" name="${_csrf.parameterName}" value = "${_csrf.token}" />          
</form>
<ul data-role="listview" data-theme="a" data-divider-theme="a" style="margin-top: -16px;" class="nav-search">
    <li data-icon="delete" style="background-color: #111;"><a href="#" data-rel="close">Close menu</a></li>
    <li><a href="${pageContext.request.contextPath}/search" data-ajax="false">Search</a></li>
    <li><a href="${pageContext.request.contextPath}/profile" data-ajax="false">Profile</a></li>
    <li><a href="#" onclick="document.logoutform.submit();">Logout</a></li>
</ul>