jquery-mobile - 为什么有些 URL 被“禁止”,而有些却不在我的网络应用程序中
问题描述
我有一个遇到奇怪行为的 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) { /* ... */ }
});
---编辑--- csrf的jQuery
$(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 顺便说一句。
解决方案
问题是因为 jQuery Mobile 通常不会在每个页面请求上加载标头信息,这是存储 CSRF 令牌的位置。因此,当导航到新页面时,它在执行 POST 时使用了陈旧的 CSRF 令牌,这会导致 403 Forbidden。为了克服这个问题,我强迫 JQM 在没有 ajax 的情况下进行data-ajax="false"
链接,方法是在每个指向页面的链接中包含。例如:
<%@ 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>
推荐阅读
- swift - 如何在 Xcode 11 中同时启用 iCloud 和密钥共享功能?
- powershell - Get-ADGroupMember 和成员的 extensionAttribute5
- docusignapi - 使用带有嵌入签名和休息 api 的 signerMustLoginToSign 时出现问题
- php - 在 PHP 中检测空二维数组的正确语法是什么?
- javascript - 如何处理与nodejs中的内存分配相关的崩溃?
- python - 条形图标签顺序错误
- c# - c#中的多维数组定义(区别及应用)
- javascript - 编写此 React 代码的最佳方式是什么?
- php - 从关联数组中置顶
- python - 为什么 10 和 5 返回 5 和 5 和 10 在 python 中返回 10?基本上,它返回和运算符之后的第二个 int,而与值无关