我对Java和Spring 3(在过去的8年中主要使用PHP)很陌生。我已经有了Spring Security 3,可以与所有默认的userDetails和userDetailsS​​ervice一起使用,并且我知道我可以使用以下命令在 Controller 中访问登录用户的用户名:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); //get logged in username

但是有两个我无法解决的问题:
  • 我想在用户登录时存储许多其他用户详细信息(例如DOB,性别等),以后可以通过 Controller 进行访问。我需要怎么做才能使创建的userDetails对象包含我的自定义字段?
  • 我已经在调用“HttpSession session = request.getSession(true);”在 Controller 中每个方法的顶部。登录时是否可以将登录用户的userDetails存储在 session 中,这样我就不必调用“身份验证auth = SecurityContextHolder.getContext()。getAuthentication();”)在每种方法的开始?

  • 安全性applicationContext.xml:
    <global-method-security secured-annotations="enabled"></global-method-security>
    <http auto-config='true' access-denied-page="/access-denied.html">
        <!-- NO RESTRICTIONS -->
        <intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/*.html" access="IS_AUTHENTICATED_ANONYMOUSLY"  />
        <!-- RESTRICTED PAGES -->
        <intercept-url pattern="/admin/*.html" access="ROLE_ADMIN" />
        <intercept-url pattern="/member/*.html" access="ROLE_ADMIN, ROLE_STAFF" />
    
        <form-login login-page="/login.html"
                    login-processing-url="/loginProcess"
                    authentication-failure-url="/login.html?login_error=1"
                    default-target-url="/member/home.html" />
        <logout logout-success-url="/login.html"/>
    </http>
    
    <authentication-manager>
        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource" authorities-by-username-query="SELECT U.username, UR.authority, U.userid FROM users U, userroles UR WHERE U.username=? AND U.roleid=UR.roleid LIMIT 1" />
            <password-encoder hash="md5"/>
        </authentication-provider>
    </authentication-manager>
    

    login.jsp:
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
    <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
    
    <tiles:insertDefinition name="header" />
    <tiles:insertDefinition name="menu" />
    <tiles:insertDefinition name="prebody" />
    
    <h1>Login</h1>
    
    <c:if test="${not empty param.login_error}">
        <font color="red"><c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/>.<br /><br /></font>
    </c:if>
    <form name="f" action="<c:url value='/loginProcess'/>" method="POST">
        <table>
            <tr><td>User:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>' /></td></tr>
                <tr><td>Password:</td><td><input type='password' name='j_password' /></td></tr>
                <tr><td>&nbsp;</td><td><input type="checkbox" name="_spring_security_remember_me" /> Remember Me</td></tr>
                <tr><td>&nbsp;</td><td><input name="submit" type="submit" value="Login" /></td></tr>
            </table>
        </form>
    
    <tiles:insertDefinition name="postbody" />
    <tiles:insertDefinition name="footer" />
    

    最佳答案

    这个问题有很多事情要做。我会尽力解决...

    问题1:这里有两种可能的方法。

    方法1:如果要向UserDetails对象添加其他属性,则应提供自己的UserDetails接口(interface)替代实现,其中包括这些属性以及相应的getter和setter。这将要求您还提供自己的UserDetailsS​​ervice接口(interface)的替代实现。该组件将必须了解如何将这些附加属性持久保存到基础数据存储中,或者当从该数据存储中读取数据时,必须了解如何填充这些附加属性。您将像这样将所有这些连接起来:

    <beans:bean id="userDetailsService" class="com.example.MyCustomeUserDetailsService">
    <!-- ... -->
    </beans:bean>
    
    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="authenticationProvider"/>
    </authentication-manager>
    
    <beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="userDetailsService" ref="userDetailsService"/>
    </beans:bean>
    

    方法2:像我一样,您可能会发现(特别是在多次迭代的跨度中)可以更好地将特定于域的用户/帐户详细信息与特定于Spring Security的用户/帐户详细信息分开。对于您而言,可能是这样,也可能不是。但是,如果您能从这种方法中找到任何智慧,那么您可以坚持使用当前的设置,并添加一个附加的User/Account域对象,相应的存储库/DAO等。如果您要检索特定于域的用户/帐户,您可以按照以下步骤进行操作:
    User user = userDao.getByUsername(SecurityContextHolder.getContext().getAuthentication().getName());
    

    问题2:Spring Security会在 session 中自动存储UserDetails(除非您已明确采取措施覆盖该行为)。因此,您无需在每个 Controller 方法中自己执行此操作。实际上,您一直在处理的SecurityContextHolder对象(由SS)在每个请求的开始时都由SecurityContext填充(包括Authentication对象,UserDetails等)。在每次请求结束时都会清除此上下文,但是数据始终保留在 session 中。

    但是,值得注意的是,如果可以避免的话,在Spring MVC Controller 中处理HttpServletRequest,HttpSession对象等并不是一个好习惯。 Spring几乎总是提供更干净,更惯用的方法来实现目标,而无需这样做。这样做的好处是, Controller 方法签名和逻辑不再依赖于在单元测试中难以模拟的事物(例如HttpSession),而不是依赖于您自己的域对象(或这些域对象的桩/模拟) )。这极大地增加了 Controller 的可测试性……从而增加了您实际将对 Controller 进行测试的可能性。 :)

    希望这会有所帮助。

    关于 Spring 安全: custom userdetails,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10607696/

    10-09 03:58