【问题标题】:Can I push my own User object into Spring's SecurityContext?我可以将自己的用户对象推入 Spring 的 SecurityContext 吗?
【发布时间】:2014-04-15 12:45:26
【问题描述】:

我是 Spring Security 的新手,并且遵循了一些基本的方法来让 Spring Security 在我的应用程序中工作,但现在我想看看是否有办法让我自己的 User 对象在登录时添加到 Spring 的 SecurityContext /身份验证。

我的安全当前配置为使用 JdbcDaoImpl:

<authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="com.ia.security.SpringSecurityDao" />
</authentication-manager>

<beans:bean id="com.ia.security.SpringSecurityDao" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    <beans:property name="usersByUsernameQuery">
        <beans:value>select username,password,enabled 
        from user 
        where username = ?
        </beans:value>
    </beans:property>
    <beans:property name="dataSource" ref="dataSource" />
    <beans:property name="enableGroups" value="true" />
    <beans:property name="enableAuthorities" value="false" />
    <beans:property name="groupAuthoritiesByUsernameQuery">
        <beans:value>SELECT R.ID, R.NAME, P.NAME
            FROM ROLE R
            JOIN USER_ROLE UR on R.id = UR.role_id
            JOIN USER U on U.id = UR.user_id
            JOIN ROLE_PERMISSION RP ON RP.role_id = R.id
            JOIN PERMISSION P ON P.id = RP.permission_id
            WHERE U.username=?
        </beans:value>
    </beans:property>
</beans:bean>

我意识到我可以从 SecurityContext 中检索 Principal 对象并获取用户名并根据用户名重新查询数据库,但我认为将整个 User 对象简单地存储在 SecurityContext 中会更容易当我在整个应用程序中需要它时,可以轻松访问它,而不仅仅是将用户名、密码和启用的字段存储在 UserDetails 对象中。

我查看了 UserDetailsS​​ervice,更具体地说是 JdbcDaoImpl 类,但不完全确定最好的方法。如果我只是通过调用super.loadUserByUsername 来覆盖/扩展loadUserByUsername 方法来返回我自己的UserDetails 对象就足够了吗?那我能不能做到SecurityContextHolder.getContext().getAuthentication().getDetails() 并将其转换为我自己的对象?

我在 StackOverflow 上找到了与此相关的其他帖子,但大多数似乎都忽略了与从数据库中检索到的权限和角色有关的任何内容,所以我不确定这是否是最好的方法.

【问题讨论】:

    标签: java spring spring-security


    【解决方案1】:

    简短的回答: 是的,你可以完全按照你的计划去做。请记住,某些功能(例如“hasRole”)默认检查权限列表而不是用户详细信息。

    长答案: 将用户对象保存在会话中的安全上下文中可能会产生一些副作用。 当您使用休眠时尤其如此。懒惰的例外任何人? ;) 我们先走这条路,后来打了几个电话,发生了一些 LIE,这很难跟进。使用 OpenSessionInView 过滤器,我们认为我们是安全的,但这是错误的,因为在下一个请求中会话消失了。所以我们从用户那里加载了更多相关的对象,它仍然发生了。稍后:)

    我们有三个选择。要么在每个请求上合并用户对象,要么在 userdetails 中创建一个仅包含必要安全信息的 pojo,要么将主体(登录)保留在安全上下文中,并在需要时加载用户对象。

    我们采用了第三种解决方案,因为 hibernate 使用二级缓存做得很好。 作为副作用,安全性现在更加可靠,因为 Spring Security 现在在每个请求上都获得了最新版本的用户,并且不适用于“陈旧”的用户角色。

    因此,如果您不使用休眠,或者您可以保证不会发生 LIE,请选择解决方案一或二。否则我会推荐我们的方法。

    【讨论】:

    • 感谢您的提醒。虽然我必须承认我对首字母缩写词 LIE 并不熟悉;我认为它与延迟初始化异常(或类似的东西)有关。实际上,我正在使用 Hibernate,并希望将 User 对象保留在 SecurityContext 中以供参考。更具体地说,我有需要当前登录用户的审计字段,并且认为只使用 SecurityContext 的 User 对象来检索它比每次都从数据库中检索它更容易。你能告诉我更多关于你遇到的 LIE 的细节/经验吗?
    • 我刚刚重新阅读了您的答案,并且对“......更可靠,因为 spring security 现在在每个请求中获取用户的最新版本......”感到困惑。那个怎么样? SpringSecurity 将在您登录时查询数据库并将该信息存储在 SecurityContext 中,包括任何权限。对于 SecurityContext 中数据的陈旧性,您是否将整个 User 实体保留在 UserDetails 中有何不同?
    • 由于我们只在安全上下文中保留登录名,我们必须在每个请求上加载用户实体。由于这会获取用户的“最新”版本,并且现在这是一个附加实体,因此我们不会遇到惰性异常。现在关于当局;回想一下,我不确定我们是否更改了当局检查的行为以从用户实体分配的角色中查找它,或者我们是否也做了其他事情来使用正确的版本。很高兴讨论这个问题,但不是在 cmets 中 :)
    • 在阅读了您的 cmets 和建议后,我最终遵循了您的建议。我没有将整个 User 对象推送到 SecurityContext 中,而是简单地存储了 userId 以方便缓存和加快从 L2 缓存中查找的速度。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    相关资源
    最近更新 更多