概述
介绍
Spring Security
使用接口SecurityContext
抽象建模"安全上下文"这一概念。这里安全上下文SecurityContext
指的是当前执行线程使用的最少量的安全信息(其实就是用于代表访问者账号的有关信息)。当一个线程在服务用户期间,该安全上下文对象会保存在SecurityContextHolder
中。
SecurityContextHolder
类提供的功能是保持SecurityContext
,不过它的用法不是让使用者创建多个SecurityContextHolder
对象,而是提供一组公开静态工具方法。基于这组方法,SecurityContextHolder
主要提供了两种管理SecurityContext
的模式 :
- 全局模式
对整个应用公开保持一个
SecurityContext
,这种模式下,应用中的多个线程同一时间通过SecurityContextHolder
访问到的都会是同一个SecurityContext
对象; - 线程本地模式
对应用中的某个线程保持一个
SecurityContext
,这种模式下,应用中的每个线程同一时间通过SecurityContextHolder
访问到的都是关于自己线程的SecurityContext
;
SecurityContext
中保存的"最少量的安全信息"其实是通过Authentication
对象所携带的信息。它用于代表当前访问者。如果当前访问者尚未认证但正在认证,Authentication
内包含的是认证请求信息,比如用户名密码等等。如果当前访问者已经被认证,则Authentication
会包含更多当前访问者的信息,比如权限,用户详情等等。另外即使访问者一直在访问一些不需要登录认证的公开资源,也有可能存在Authentication
对象,此时Authentication
会是一种特殊的类型,专门用于表示这是一个匿名用户。
Spring Security
为接口SecurityContext
提供了一个缺省实现SecurityContextImpl
并在框架内各处缺省。
使用
// HttpServlet3RequestFactory 代码片段
@Override
public void login(String username, String password) throws ServletException {
// ...
Authentication authentication;
try {
authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
}
catch (AuthenticationException loginFailed) {
// 认证失败,清除 SecurityContextHolder 中的安全上下文
SecurityContextHolder.clearContext();
throw new ServletException(loginFailed.getMessage(), loginFailed);
}
// 认证成功,设置认证对象到 SecurityContextHolder 中的安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
@Override
public void logout() throws ServletException {
LogoutHandler handler = HttpServlet3RequestFactory.this.logoutHandler;
// ...
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
// 退出登录处理器也需要从 SecurityContextHolder 中获取安全上下文中的认证对象进行处理
handler.logout(this, this.response, authentication);
}
源代码
源代码版本 Spring Security Web 5.1.4.RELEASE
SecurityContext
接口
package org.springframework.security.core.context;
import org.springframework.security.core.Authentication;
import java.io.Serializable;
/**
* Interface defining the minimum security information associated with the current thread
* of execution.
*
*
* The security context is stored in a SecurityContextHolder.
*
*
* @author Ben Alex
*/
public interface SecurityContext extends Serializable {
// ~ Methods
// ============================================================
/**
* Obtains the currently authenticated principal, or an authentication request token.
*
* @return the Authentication or null if no authentication
* information is available
*/
Authentication getAuthentication();
/**
* Changes the currently authenticated principal, or removes the authentication
* information.
*
* @param authentication the new Authentication token, or
* null if no further authentication information should be stored
*/
void setAuthentication(Authentication authentication);
}
缺省实现 SecurityContextImpl
Spring Security为接口
SecurityContext提供的缺省实现
SecurityContextImpl,该实现逻辑其实很简单,主要就是保持一个
Authentication`对象。
package org.springframework.security.core.context;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityCoreVersion;
/**
* Base implementation of SecurityContext.
*
* Used by default by SecurityContextHolder strategies.
*
* @author Ben Alex
*/
public class SecurityContextImpl implements SecurityContext {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
// ~ Instance fields
// ======================================================
private Authentication authentication;
public SecurityContextImpl() {}
public SecurityContextImpl(Authentication authentication) {
this.authentication = authentication;
}
// ~ Methods
// ======================================================
@Override
public boolean equals(Object obj) {
if (obj instanceof SecurityContextImpl) {
SecurityContextImpl test = (SecurityContextImpl) obj;
if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
return true;
}
if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
&& this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
}
return false;
}
@Override
public Authentication getAuthentication() {
return authentication;
}
@Override
public int hashCode() {
if (this.authentication == null) {
return -1;
}
else {
return this.authentication.hashCode();
}
}
@Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
if (this.authentication == null) {
sb.append(": Null authentication");
}
else {
sb.append(": Authentication: ").append(this.authentication);
}
return sb.toString();
}
}