Почему некоторые URL-адреса "запрещены", а некоторые нет в моем веб-приложении - PullRequest
0 голосов
/ 07 мая 2019

У меня есть веб-приложение со странным поведением.Когда вы пытаетесь вызвать приложение, оно запрашивает, чтобы вы вошли в систему, как ожидается, и переносит вас на страницу приветствия (/), после чего вы можете выбрать либо страницу профиля (/profile), либо страницу поиска (* 1003).*).Если вы попытаетесь получить доступ к любой из этих страниц без входа в систему, он перенаправит вас на страницу входа в систему, как и ожидалось.Когда вы пытаетесь отправить критерии поиска или изменить пароль, возвращается 403 Запрещено.

<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>

urls:

/                       (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) { /* ... */ }
});

--- EDIT --- jQuery для 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, кстати.

1 Ответ

1 голос
/ 08 мая 2019

Проблема вызвана тем, что jQuery Mobile обычно не загружает информацию заголовка при каждом запросе страницы, где хранится токен CSRF.Поэтому при переходе на новую страницу он использует устаревший токен CSRF при выполнении POST, что вызывает 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>
...