问题描述
我有一个自定义的 UserDetailService
实现器,即 MyCustomUserDetailService
,其中 loadUserByName 方法被覆盖.
我有生成 JWT 令牌的令牌生成器类.
我有一个名为 JWTAuthenticationFilter
的类,它扩展了OnceperRequestFilter
我所在的地方有一个 doFilter
方法验证已生成和提供的令牌客户端在 HttpRequest 的标头中.现在从这里开始我真正的问题.代码是这样的:
UserDetails usd=this.myCustomUserDetailService.loadByUserName(userNameFromToke);//userNameFromToke 是从客户端提供的令牌中提取的字符串.UsernamePasswordAuthenticationToken upat=new UsernamePasswordAuthenticationToken(usd,null,usd.getAuthorities());//这里在密码中我传递null.upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));SecurityContextHolder.getContext().setAuthentication(upat);
问题 1:
`ups.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));`
为什么我们传递整个 HTTP 请求(即 httpRequest)而不是用户详细信息我们从令牌中提取的?为什么我需要这个 setDetails
方法,因为我已经传递了用户详细信息、密码和列表UsernamePasswordAuthenticationToken
中构造函数参数中的权限.
问题 2:
setAuthentication 方法和 UsernamePasswordAuthenticationToken 是如何工作的?我正在通过我从令牌中提取的信息,即SecurityContextHolder.getContext().setAuthentication(upat)
;以及 UsernamePasswordAuthenticationToken
的构造函数中的用户详细信息、密码和权限,但是反对它验证upat"的内容?UsernamePasswordAuthenticationToken
是否在内部自动检查使用MyCustomUserDetailService
"?
我知道这个问题有点冗长,但由于我是初学者,我正在努力理解其背后的机制,我已经对其进行了研究,但这种困惑并没有消失.请帮忙.另外,如果您觉得我的问题有问题,请建议我如何简明我的问题.
UsernamePasswordAuthenticationToken
中的详细信息完全由您决定,如果您稍后在应用程序中需要来自经过身份验证的用户的更多详细信息,它会有所帮助.就像您调用SecurityContextHolder.getContext().getAuthentication()
时一样,您可以将此Authentication
转换为UsernamePasswordAuthenticationToken
并出于任何业务目的访问其详细信息;您的
UsernamePasswordAuthenticationToken
未针对任何内容进行验证,您需要通过调用您的UserDetailsService
并检查密码或注入并调用来手动进行验证code>AuthenticationManager#authenticate
方法.Spring Security 不在乎SecurityContextHolder
是如何填充的.在基本场景中,在您将经过身份验证的SecurityContext
设置到SecurityContextHolder
后,SecurityContextPersistenceFilter
将获取此SecurityContext
并保存它在HttpSession
中作为一个属性.在接下来的请求中,此属性将出现在您的HttpSession
中,这样 Spring Security 会加载SecurityContext
并将其设置到SecurityContextHolder
中.
您可以在 SecurityContextPersistenceFilter
中获得更多详细信息 实现,但我会在这里指出这个问题的具体部分:
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);//这里 Spring Security 从 HttpSession 加载 SecurityContext,这是在到达你的控制器之前,在调用 FilterChain 之前完成的.SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);试试{SecurityContextHolder.setContext(contextBeforeChainExecution);if (contextBeforeChainExecution.getAuthentication() == null) {logger.debug(将 SecurityContextHolder 设置为空 SecurityContext");}其他{如果 (this.logger.isDebugEnabled()) {这个记录器.debug(LogMessage.format(Set SecurityContextHolder to %s", contextBeforeChainExecution));}}chain.doFilter(holder.getRequest(), holder.getResponse());}最后{//您已经填充了 SecurityContextHolder,并且在您的应用程序返回后它将在此处可用.所以它会得到 SecurityContext 并将其保存在 HttpSession 中SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();SecurityContextHolder.clearContext();//在 HttpSession 中持久化 SecurityContextthis.repo.saveContext(contextAfterChainExecution,holder.getRequest(),holder.getResponse());request.removeAttribute(FILTER_APPLIED);this.logger.debug(已清除 SecurityContextHolder 以完成请求");}
我希望这能帮助您更好地理解 Spring Security 的工作原理.还有一个参考文档,这是一个了解架构细节的重要来源.
I have a custom UserDetailService
implementor i.e. MyCustomUserDetailService
where loadUserByName method is overridden.
I have token generator class that generates the JWT token.
UserDetails usd=this.myCustomUserDetailService.loadByUserName(userNameFromToke);//userNameFromToke is a String that got extracted from the token that client provided.
UsernamePasswordAuthenticationToken upat=new UsernamePasswordAuthenticationToken(usd,null,usd.getAuthorities()); //Here in the password I am passing null.
upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(upat);
I know the question is a bit lengthy but as I am a beginner I am trying hard to understand the mechanism behind it and I have done research on it but this confusion are not getting away. Please help. Also, suggest me how can I concise my question if you seem it's faulty.
The details in the
UsernamePasswordAuthenticationToken
are totally up to you, it helps if you need more details from the authenticated user later on in your application. Like when you callSecurityContextHolder.getContext().getAuthentication()
, you can cast thisAuthentication
toUsernamePasswordAuthenticationToken
and access its details for any business purposes;Your
UsernamePasswordAuthenticationToken
is not being validated against anything, you need to do it manually by either calling yourUserDetailsService
and checking the password, or injecting and callingAuthenticationManager#authenticate
method. Spring Security does not care how theSecurityContextHolder
was populated. In basic scenarios, after you setting an authenticatedSecurityContext
into theSecurityContextHolder
, theSecurityContextPersistenceFilter
will take thisSecurityContext
and save it in theHttpSession
as an attribute.In the next requests, this attribute will be present in yourHttpSession
, this way Spring Security loads theSecurityContext
and sets it into theSecurityContextHolder
.
You can get more details in the SecurityContextPersistenceFilter
implementation, but I'll point out the specific part for this question here:
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
// Here Spring Security loads the SecurityContext from the HttpSession, this is done before reaching your controllers, before calling the FilterChain.
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
if (contextBeforeChainExecution.getAuthentication() == null) {
logger.debug("Set SecurityContextHolder to empty SecurityContext");
}
else {
if (this.logger.isDebugEnabled()) {
this.logger
.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));
}
}
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
// You've populated the SecurityContextHolder and it'll be available here after you application returns. So it'll get the SecurityContext and persist it in the HttpSession
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
// Persisting the SecurityContext in the HttpSession
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
I hope this helps you to understand better how Spring Security works. There is also a reference documentation that is a great source to know more about the details of the architecture.
这篇关于Spring Boot 中 Spring Security 的困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!