我的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。
答案 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>