本文介绍了Spring Security:自定义用户详细信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm pretty new to Java and Spring 3 (used primarily PHP the past 8 years). I've gotten spring security 3 to work with all the default userDetails and userDetailsService and I know I can access the logged in user's username in a controller by using:

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

但是我不知道有两个问题:

But there are two problems I can't figure out:

  1. 我还希望在用户登录时存储许多其他用户详细信息(例如DOB,性别等),以后可以通过控制器进行访问.我该怎么做才能使创建的userDetails对象包含我的自定义字段?

  1. There are a lot of other user details I would like stored when a user logs in (such as DOB, gender, etc.) and to be accessible via the controllers later on. What do I need to do so that the userDetails object that is created contains my custom fields?

我已经在调用"HttpSession会话= request.getSession(true);"在控制器中每个方法的顶部.登录时是否可以将登录用户的userDetails存储在会话中,这样我就不必调用身份验证auth = SecurityContextHolder.getContext().getAuthentication();")在每种方法的开头?

I'm already calling "HttpSession session = request.getSession(true);" at the top of each of my methods in my controller. Is it possible to store the logged in user's userDetails in a session upon login so that I don't need to also call "Authentication auth = SecurityContextHolder.getContext().getAuthentication();" at the beginning of every method?

Security-applicationContext.xml:

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

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

推荐答案

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

There's an awful lot going on in this question. I'll try to address it in pieces...

Q#1:这里有两种可能的方法.

Q#1: There are a couple possible approaches here.

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

Approach #1: If you have other attributes that you want to add to your UserDetails object, then you should provide your own alternate implementation of the UserDetails interface that includes those attributes along with corresponding getters and setters. This would require that you also provide your own alternate implementation of the UserDetailsService interface. This component would have to understand how to persist these additional attributes to the underlying datastore, or when reading from that datastore, would have to understand how to populate those additional attributes. You'd wire all of this up like so:

<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等.如果要检索特定于域的用户/帐户,您可以按照以下步骤操作:

Approache #2: Like me, you may find (especially over the span of several iterations) that you're better served to keep domain-specific user/account details separate from Spring Security specific user/account details. This may or may not be the case for you. But if you can find any wisdom in this approach, then you'd stick with the setup you have currently and add an additional User/Account domain object, corresponding repository/DAO, etc. If you want to retrieve the domain-specific user/account, you can do so as follows:

User user = userDao.getByUsername(SecurityContextHolder.getContext().getAuthentication().getName());

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

Q#2: Spring Security automatically stores the UserDetails in the session (unless you've explicitly taken steps to override that behavior). So there's no need for you to do this yourself in each of your controller methods. The SecurityContextHolder object you've been dealing with is actually populated (by SS) with SecurityContext including the Authentication object, UserDetails, etc. at the beginning of every request. This context is cleared at the end of each request, but the data always remains in the session.

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

It's worth noting, however, that it's not really a great practice to be dealing with HttpServletRequest, HttpSession objects, etc. in a Spring MVC controller if you can avoid it. Spring almost always offers cleaner, more idiomatic means of achieving things without the need for doing so. The advantage to that would be that controller method signatures and logic cease to be dependent on things that are difficult to mock in a unit test (e.g. the HttpSession) and instead of dependent on your own domain objects (or stubs/mocks of those domain objects). This drastically increases the testability of your controllers... and thus increases the liklihood that you actually WILL test your controllers. :)

希望这对您有所帮助.

这篇关于Spring Security:自定义用户详细信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 10:44