下面开始讨论几个 Spring Security 里面的核心对象。
org.springframework.security.core.context.SecurityContext
接口表示的是当前应用的安全上下文。通过此接口可以获取和设置当前的认证对象。org.springframework.security.core.Authentication
接口用来表示此认证对象。通过认证对象的方法可以判断当前用户是否已经通过认证,以及获取当前认证用户的相关信息,包括用户名、密码和权限等。
要使用Authentication认证对象,首先需要获取到 SecurityContext 对象。通过 org.springframework.security.core.context.SecurityContextHoldergetContext()
的静态方法 就可以获取。再通过 SecurityContext.getAuthentication()
就可以得到Authentication
。通过认证对象的 Authentication.getPrincipal()
方法就可以获得当前的认证主体
。
认证主体
通常是 UserDetails 接口的实现,典型的认证过程就是当用户输入了用户名和密码之后,UserDetailsService
通过用户名找到对应的 UserDetails
对象,接着比较密码是否匹配。如果不匹配,则返回出错信息;如果匹配的话,说明用户认证成功,就创建一个实现了 Authentication接口的对象,如 org.springframework.security. authentication.UsernamePasswordAuthenticationToken
类的对象。再通过 SecurityContext.setAuthentication()
方法来设置此认证对象。
//获取当前认证用户的用户名
public static String getAuthenticatedUsername() {
String username = null;
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else {
username = principal.toString();
}
return username;
}
默认情况下(Web应用来说),SecurityContextHolder
使用 ThreadLocal
来保存 SecurityContext对象。因此,SecurityContext对象对于当前线程上所有方法都是可见的。这种实现对于 Web 应用来说是合适的。
不过在有些情况下,如桌面应用,这种实现方式就不适用了。Spring Security 允许开发人员对此进行定制。开发人员只需要实现接口 org.springframework.security.core.context.SecurityContextHolderStrategy
并通过 SecurityContextHolder.setStrategyName(String)
方法让 Spring Security 使用此实现即可。
另外一种设置方式是使用系统属性。Spring Security 默认提供了另外两种实现方式:
SecurityContextHolder.MODE_GLOBAL
表示当前应用共享唯一的 SecurityContextHolder;SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
表示子线程继承父线程的 SecurityContextHolder。
//使用全局唯一的 SecurityContextHolder的示例。
public void useGlobalSecurityContextHolder() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL);
}