spring security的核心功能:认证和授权,认证就是身份验证(你是谁?),授权就是访问控制(你可以做什么?),同时他还提供很多安全管理的周边功能。在Spring Security的架构设计中,认证(Authentication)和授权(Authorization)是分开的,本篇文章主要涉及认证相关的功能和原理介绍。
当用户登录后,都会对应一个不同的Authentication实例,最常用的实现类是UsernamePasswordAuthenticationToken和RememberMeAuthenticationToken
- public interface Authentication extends Principal, Serializable {
-
- //获取用户权限列表
- Collection extends GrantedAuthority> getAuthorities();
-
- //获取用户凭证,一般来说就是密码,认证成功后密码一般会被删除
- Object getCredentials();
-
- //获取用户的详细信息,可能是当前请求之类等
- Object getDetails();
-
- //获取当前用户,例如一个用户名或者一个用户对象
- Object getPrincipal();
-
- boolean isAuthenticated();
-
- void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
- }
authenticate方法用来做认证,返回Authentication表示认证成功,抛出异常表示用户输入无效的凭证,返回null表示不能确定
- public interface AuthenticationManager {
- Authentication authenticate(Authentication authentication) throws AuthenticationException;
- }
ProviderManager是AuthenticationManager的实现类,ProviderManager管理众多AuthenticationProvider实例(详见2.4节),AuthenticationProvider会有一个supports方法,用来判断当前AuthenticationProvider是否支持此次认证请求。
ProviderManager的大致认证流程就是遍历众多AuthenticationProvider实例(例如有的是表单登陆,有的是短信验证码登陆)。
- //省略部分代码
- public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
-
- private AuthenticationEventPublisher eventPublisher;
-
- private List
providers; -
- private AuthenticationManager parent;
-
- private boolean eraseCredentialsAfterAuthentication;
-
- public ProviderManager(AuthenticationProvider... providers) {
- this(Arrays.asList(providers), (AuthenticationManager)null);
- }
-
- public ProviderManager(List
providers) { - this(providers, (AuthenticationManager)null);
- }
-
- public ProviderManager(List
providers, AuthenticationManager parent) { - }
-
- //核心部分,省略部分代码
- public Authentication authenticate(Authentication authentication) throws AuthenticationExceptio {
- Class extends Authentication> toTest = authentication.getClass();
- AuthenticationException lastException = null;
- AuthenticationException parentException = null;
- Authentication result = null;
- Authentication parentResult = null;
- int currentPosition = 0;
- int size = this.providers.size();
- for (AuthenticationProvider provider : getProviders()) {
- result = provider.authenticate(authentication);
- if (result != null) {
- copyDetails(authentication, result);
- break;
- }
- }
- //如果providers都不能处理,由parent处理
- if (result == null && this.parent != null) {
- // Allow the parent to try.
- try {
- parentResult = this.parent.authenticate(authentication);
- result = parentResult;
- }
- catch (AuthenticationException ex) {
- parentException = ex;
- lastException = ex;
- }
- }
- if (result != null) {
- if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
- //擦除密码,发布事件
- ((CredentialsContainer) result).eraseCredentials();
- }
- if (parentResult == null) {
- this.eventPublisher.publishAuthenticationSuccess(result);
- }
- return result;
- }
- }
- }
AuthenticationProvider有点类似AuthenticationManager,但是多了一个supports方法用来判断是否支持给定的Authentication类型
- public interface AuthenticationProvider {
-
- Authentication authenticate(Authentication authentication) throws AuthenticationException;
-
- boolean supports(Class> authentication);
- }
规范开发者自定义用户对象
- public interface UserDetails extends Serializable {
-
- //返回当前账户具备的权限
- Collection extends GrantedAuthority> getAuthorities();
-
- String getPassword();
-
- String getUsername();
-
- boolean isAccountNonExpired();
-
- boolean isAccountNonLocked();
-
- boolean isCredentialsNonExpired();
-
- boolean isEnabled();
- }
负责提供用户数据源,提供查询用户的方法
- public interface UserDetailsService {
- UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
- }
在Spring Security中,认证和授权都是基于过滤器来完成的,过滤器通过FilterChainProxy来统一代理,然后将FilterChainProxy嵌入到web项目的原生过滤器中。FilterChainProxy本身通过Spring框架提供的DelegatingFilterProxy整合到原生过滤器链中。

| Filter | 用途 |
|---|---|
| ChannelProcessingFilter | 用于通道处理的 |
| WebAsyncManagerIntegrationFilter | 异步集成 |
| CorsFilter | 跨域资源共享 |
| CsrfFilter | 处理跨域站点伪造 |
| LogoutFilter | 注销 |
| CorsFilter | 跨域资源共享 |
| OAuth2AuthorizationRequestRedirectFilter | OAuth2认证请求重定向 |
| X509AuthenticationFilter | X509证书认证 |
| UsernamePasswordAuthenticationFilter | 用户名密码认证 |
| ConcurrentSessionFilter | 并发会话 |
| BearerTokenAuthenticationFilter | Bearer令牌认证 |
| RememberMeAuthenticationFilter | 记住我认证 |
| AnonymousAuthenticationFilter | 匿名认证 |
| ExceptionTranslationFilter | 异常转移 |
| SwitchUserFilter | 切换用户 |
如果不使用安全框架,可以通过HttpSession读写用户数据,Spring Security对HttpSession的用户信息进行了封装,开发者获取用户信息的方式有两种:
- @Controller
- public class HelloController {
-
- @RequestMapping(method = RequestMethod.GET, path = "principal")
- @ResponseBody
- public Object principal() {
- SecurityContext context = SecurityContextHolder.getContext();
- Authentication authentication = context.getAuthentication();
- Object principal = authentication.getPrincipal();
- return principal;
- }
- }
- @RequestMapping("/authentication")
- public void authentication(Authentication authentication) {
- System.out.println("authentication = " + authentication);
- }
- @RequestMapping("/principal")
- public void principal(Principal principal, HttpServletRequest req) {
- System.out.println("req.getClass() = " + req.getClass());
- System.out.println("principal = " + principal);
- }
- @RequestMapping("/info")
- public void info(HttpServletRequest req) {
- String remoteUser = req.getRemoteUser();
- Authentication auth = ((Authentication) req.getUserPrincipal());
- boolean admin = req.isUserInRole("admin");
- System.out.println("remoteUser = " + remoteUser);
- System.out.println("auth.getName() = " + auth.getName());
- System.out.println("admin = " + admin);
- }