栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Spring Security中的每个请求都从数据库重新加载UserDetails对象

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Security中的每个请求都从数据库重新加载UserDetails对象

最后,两年后,对于上述问题,以及此问题之后的六年,这里是有关如何使用Spring重新为每个请求重新加载用户的UserDetails的答案。

为了按请求重新加载用户/安全上下文,重要的是重写Spring
Security的HttpSessionSecurityContextRepository的默认行为,该行为实现了SecurityContextRepository接口。

HttpSessionSecurityContextRepository是Spring
Security用来从HttpSession获取用户的安全上下文的类。调用此类的代码是将SecurityContext置于threadlocal上的原因。因此,在

loadContext(HttpRequestResponseHolderrequestResponseHolder)
调用该方法时,我们可以转过来向DAO或存储库发出请求,然后重新加载用户/主要用户。

尚未完全解决一些令人关注的问题。

此代码线程安全吗?

我不知道,这取决于是否有针对Web服务器中每个线程/请求创建的新SecurityContext。如果有一个新的SecurityContext创建的生活是美好的,但如果不是,那么可能会有一些有趣的意外行为,例如陈旧的对象异常,错误的用户/主体状态保存到数据存储等。

我们的代码“足够低风险”,因此我们没有尝试测试潜在的多线程问题。

每次请求调用数据库都会对性能产生影响吗?

很有可能,但我们尚未看到Web服务器响应时间的明显变化。

关于此主题的几点快速注释…

  • 数据库是超级智能的,它们具有知道什么以及何时缓存特定查询的算法。
  • 我们正在使用hibernate的二级缓存。

我们从这项变更中获得的好处:

  • 过去我们用来表示Principal的UserDetails对象不是可序列化的,因此,当我们停止并重新启动tomcat服务器时,所有反序列化的SercurityContexts都将具有空的Principal对象,并且最终用户将收到服务器错误,原因是空指针异常。现在,UserDetails / Principal对象是可序列化的,并且根据请求重新加载了用户,我们可以启动/重新启动服务器,而不必清理工作目录。
  • 我们收到零投诉,称他们的新权限没有立即生效。

代码

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContext;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.openid.OpenIDAuthenticationToken;import org.springframework.security.web.context.HttpRequestResponseHolder;import org.springframework.security.web.context.HttpSessionSecurityContextRepository;import xxx.repository.security.UserRepository;import xxx.model.security.User;import xxx.service.security.impl.acegi.AcegiUserDetails;public class ReloadUserPerRequestHttpSessionSecurityContextRepository extends HttpSessionSecurityContextRepository {    // Your particular data store object would be used here...    private UserRepository userRepository;    public ReloadUserPerRequestHttpSessionSecurityContextRepository(UserRepository userRepository) {        this.userRepository = userRepository;    }    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {        // Let the parent class actually get the SecurityContext from the HTTPSession first.        SecurityContext context = super.loadContext(requestResponseHolder);        Authentication authentication = context.getAuthentication();        // We have two types of logins for our system, username/password        // and Openid, you will have to specialize this pre for your particular application.        if (authentication instanceof UsernamePasswordAuthenticationToken) { UserDetails userDetails = this.createNewUserDetailsFromPrincipal(authentication.getPrincipal()); // Create a new Authentication object, Authentications are immutable. UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials(), userDetails.getAuthorities()); context.setAuthentication(newAuthentication);        } else if (authentication instanceof OpenIDAuthenticationToken) { UserDetails userDetails = this.createNewUserDetailsFromPrincipal(authentication.getPrincipal()); OpenIDAuthenticationToken openidAuthenticationToken = (OpenIDAuthenticationToken) authentication; // Create a new Authentication object, Authentications are immutable. OpenIDAuthenticationToken newAuthentication = new OpenIDAuthenticationToken(userDetails, userDetails.getAuthorities(), openidAuthenticationToken.getIdentityUrl(), openidAuthenticationToken.getAttributes()); context.setAuthentication(newAuthentication);        }        return context;    }    private UserDetails createNewUserDetailsFromPrincipal(Object principal) {        // This is the class we use to implement the Spring Security UserDetails interface.        AcegiUserDetails userDetails = (AcegiUserDetails) principal;        User user = this.userRepository.getUserFromSecondaryCache(userDetails.getUserIdentifier());        // NOTE:  We create a new UserDetails by passing in our non-serializable object 'User', but that object in the AcegiUserDetails is transient.        // We use a UUID (which is serializable) to reload the user.  See the userDetails.getUserIdentifier() method above.        userDetails = new AcegiUserDetails(user);        return userDetails;    }}

要使用xml配置插入新的SecurityContextRepository,只需在security:http上下文中设置security-context-
repository-ref属性。

范例XML:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:security="http://www.springframework.org/schema/security"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd     http://www.springframework.org/schema/security     http://www.springframework.org/schema/security/spring-security-4.0.xsd">    <security:http context-repository-ref="securityContextRepository" >         <!-- intercept-url and other security configuration here... -->    </security:http>    <bean id="securityContextRepository"  >        <constructor-arg index="0" ref="userRepository"/>    </bean></beans>



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/454272.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号