Spring Security:仅为经过身份验证的用户执行 jQuery 代码

Posted

技术标签:

【中文标题】Spring Security:仅为经过身份验证的用户执行 jQuery 代码【英文标题】:Spring Security: Execute jQuery Code Only For Authenticated Users 【发布时间】:2014-11-22 01:25:00 【问题描述】:

我正在关注有关使用 Spring MVC 和一些基本 Spring Security 构建网站的教程。我有以下问题:

功能

主页中的 jQuery 向 /getmessages 方法发送一个请求,该方法返回一个表示地图的 JSON 对象。 'size' 属性是消息的数量。 jQuery 然后用那个值更新一个 SPAN 标签。此 SPAN 标记包含在 <sec:authorize access="isAuthenticated()"> 标记中,因此只有登录的用户才能看到它。这是每 5 秒循环一次。

/getMessages URL 受isAuthenticated() 拦截保护。

预期行为

用户点击主页上的“登录”链接,将用户带到登录页面 用户登录成功,用户被重定向回首页。

实际行为

用户点击主页上的“登录”链接,将用户带到登录页面 用户成功登录,但用户被重定向到显示原始 JSON 数据的/getmessages URL

我认为这是因为无论用户是否登录,对 /getmessages 的 jQuery 请求都会发生。当用户登录时,Spring 会重定向到最近一次对受保护 URL 的请求尝试,在本例中为 /getmessages

问题

如何实现我的预期行为。理想情况下,如果用户未通过身份验证,我希望 jQuery 不调用 /getmessages。我已经尝试在<sec:authorize access="isAuthenticated()"> 中围绕 jQuery 的那一部分,它确实有效,但我不知道是否在 jQuery/javascript 代码中包含 sec 标记好/坏/丑陋的做法。

另一种可能性是从服务器向 jQuery 返回一个指示用户(主体)是否登录的变量,然后在 IF 语句中使用该变量来保护/getmessages 调用的入口

我很感激有关解决此问题的最佳方法的建议。

代码

主页 jQuery

<script type="text/javascript">


function getMessageCount(data)

    $("#message-count").text(data.size);        



function onLoad()

    updatePage();
    window.setInterval(updatePage, 5000);



function updatePage()



    $.getJSON("<c:url value="/getmessages"/>", getMessageCount);







$(document).ready(onLoad);


</script>

getMessages 方法

@RequestMapping(value="/getmessages", method=RequestMethod.GET, produces="application/json")
@ResponseBody
public Map<String, Object> getMessages(Principal principal)

    List<Message> messages = new ArrayList<Message>();

    if(principal != null)  
        String username = principal.getName();          
        messages = userService.getMessages(username);           
    

    Map<String, Object> data = new HashMap<String, Object>();
    data.put("messages", messages);
    data.put("size", messages.size());

    return data;


登录页面 JSP(平铺)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<script type="text/javascript">

$(document).ready(function()

      document.f.j_username.focus()

)


</script>

<h3>Login With Username and Password</h3>

<c:if test="$param.auth != null">

    <p class="error">Username or password is incorrect</p>

</c:if>

<form name='f'
    action='$pageContext.request.contextPath/j_spring_security_check'
    method='POST'>

    <table class="formtable">
        <tr>
            <td>User:</td>
            <td><input type="text" name="j_username" value=''></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="j_password" value=''></td>
        </tr>

        <tr>
            <td>Remember Me</td>
            <td><input type="checkbox" name="_spring_security_remember_me"></td>
        </tr>

        <tr>
            <td class="label"></td>
            <td><input type="submit" name="submit" value='Login'></td>
        </tr>

    </table>

</form>

<p>
    <a href="<c:url value='/newaccount' />">Create New Account</a>
</p>

安全上下文配置 XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security    http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


<security:authentication-manager>
    <security:authentication-provider>
        <security:jdbc-user-service data-source-ref="dataSource"
            authorities-by-username-query="SELECT username, authority FROM users WHERE BINARY username = ?"
            users-by-username-query="SELECT username, password, enabled FROM users WHERE BINARY username = ?" 
            id="jdbcUserService" />
        <security:password-encoder ref="passwordEncoder"></security:password-encoder>
    </security:authentication-provider>
</security:authentication-manager>


<security:http use-expressions="true">
    <security:intercept-url pattern="/admin" access="permitAll" />
    <security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
    <security:intercept-url pattern="/createoffer" access="isAuthenticated()" />
    <security:intercept-url pattern="/offerdeleted" access="isAuthenticated()" />
    <security:intercept-url pattern="/docreate" access="isAuthenticated()" />
    <security:intercept-url pattern="/offercreated" access="isAuthenticated()" />
    <security:intercept-url pattern="/getmessages" access="isAuthenticated()" />
    <security:intercept-url pattern="/" access="permitAll" />
    <security:intercept-url pattern="/loggedout" access="permitAll" />
    <security:intercept-url pattern="/message" access="permitAll" />
    <security:intercept-url pattern="/static/**" access="permitAll" />
    <security:intercept-url pattern="/newaccount" access="permitAll" />
    <security:intercept-url pattern="/createaccount" access="permitAll" />
    <security:intercept-url pattern="/login" access="permitAll" />
    <security:intercept-url pattern="/offers" access="permitAll" />
    <security:intercept-url pattern="/denied" access="permitAll" />
    <security:intercept-url pattern="/**" access="denyAll" />
    <security:form-login login-page="/login" authentication-failure-url="/login?auth=false" />
    <security:logout logout-success-url="/loggedout" />
    <security:access-denied-handler error-page="/denied" />
    <security:remember-me key="offersAppKey" user-service-ref="jdbcUserService" />
</security:http>


<security:global-method-security secured-annotations="enabled"></security:global-method-security>

<bean id="passwordEncoder"
    class="org.springframework.security.crypto.password.StandardPasswordEncoder">
</bean>

【问题讨论】:

我不认为将 JavaScript 代码部分包装在 &lt;sec:authorize access="isAuthenticated()"&gt; 中是不好/丑陋的做法。相反,盲目地发送需要身份验证的请求并不好。 如果你不介意总是跳转到首页,也许你想试试always-use-default-target 我第二个@qingbo。使用sec:authorize 更改 JavaScript 逻辑没有任何问题。第二种方法(在控制器中设置请求并在 JSP 中对其进行评估)同样适用。 【参考方案1】:

只需保护调用控制器以获取 JSON 数据的 JavaScript 代码块

<sec:authorize access="isAuthenticated()">
      Home Page jQuery script-block
</sec:authorize>

关注:完美保护登录用户需要查看的代码/部分。您已经正确理解了 spring-security 记得重定向受保护的 URL,onload 您正在调用该控制器,只是保护它,这意味着整个调用序列,即脚本块。

  $.getJSON("<c:url value="/getmessages"/>", getMessageCount);

【讨论】:

以上是关于Spring Security:仅为经过身份验证的用户执行 jQuery 代码的主要内容,如果未能解决你的问题,请参考以下文章

如何在 spring-security-oauth 中获取经过身份验证的用户

Spring security + JWT + 经过身份验证并且 hasRole 不起作用?

重启后 Spring Security 仍然经过身份验证(但没有会话)

具有角色的经过身份验证的用户的 Spring Security Java 配置

JAX-RS 和 Spring Security - 获取经过身份验证的用户

Spring Security:如何向经过身份验证的用户添加额外的角色