spring security 认证简介

1. 简介

spring security的核心功能:认证和授权,认证就是身份验证(你是谁?),授权就是访问控制(你可以做什么?),同时他还提供很多安全管理的周边功能。在Spring Security的架构设计中,认证(Authentication)和授权(Authorization)是分开的,本篇文章主要涉及认证相关的功能和原理介绍。

2.认证核心组件

2.1 Authentication

当用户登录后,都会对应一个不同的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;
}

2.2 AuthenticationManager

authenticate方法用来做认证,返回Authentication表示认证成功,抛出异常表示用户输入无效的凭证,返回null表示不能确定

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

2.3 ProviderManager

ProviderManager是AuthenticationManager的实现类,ProviderManager管理众多AuthenticationProvider实例(详见2.4节),AuthenticationProvider会有一个supports方法,用来判断当前AuthenticationProvider是否支持此次认证请求。

ProviderManager的大致认证流程就是遍历众多AuthenticationProvider实例(例如有的是表单登陆,有的是短信验证码登陆)。

//省略部分代码
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

    private AuthenticationEventPublisher eventPublisher;

    private List<AuthenticationProvider> providers;

    private AuthenticationManager parent;

    private boolean eraseCredentialsAfterAuthentication;

    public ProviderManager(AuthenticationProvider... providers) {
        this(Arrays.asList(providers), (AuthenticationManager)null);
    }

    public ProviderManager(List<AuthenticationProvider> providers) {
        this(providers, (AuthenticationManager)null);
    }

    public ProviderManager(List<AuthenticationProvider> 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;
		}
    }
}

2.4 AuthenticationProvider

AuthenticationProvider有点类似AuthenticationManager,但是多了一个supports方法用来判断是否支持给定的Authentication类型
public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    boolean supports(Class<?> authentication);
}

2.5 UserDetails

规范开发者自定义用户对象

public interface UserDetails extends Serializable {
    
    //返回当前账户具备的权限
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}

 2.6 UserDetailsService

负责提供用户数据源,提供查询用户的方法

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

3. Filter

在Spring Security中,认证和授权都是基于过滤器来完成的,过滤器通过FilterChainProxy来统一代理,然后将FilterChainProxy嵌入到web项目的原生过滤器中。FilterChainProxy本身通过Spring框架提供的DelegatingFilterProxy整合到原生过滤器链中。

3.1 默认的Filter顺序

其次序定义在FilterOrderRegistration类中

次序
DisableEncodeUrlFilter100
ForceEagerSessionCreationFilter200
ChannelProcessingFilter300
WebAsyncManagerIntegrationFilter500
SecurityContextHolderFilter600
SecurityContextPersistenceFilter700
HeaderWriterFilter800
CorsFilter900
CsrfFilter1000
LogoutFilter1100
OAuth2AuthorizationRequestRedirectFilter1200
Saml2WebSsoAuthenticationRequestFilter1300
X509AuthenticationFilter1400
AbstractPreAuthenticatedProcessingFilter1500
CasAuthenticationFilter1600
OAuth2LoginAuthenticationFilter1700
Saml2WebSsoAuthenticationFilter1800
UsernamePasswordAuthenticationFilter1900
DefaultLoginPageGeneratingFilter2100
DefaultLogoutPageGeneratingFilter2200
ConcurrentSessionFilter2300
DigestAuthenticationFilter2400
BearerTokenAuthenticationFilter2500
BasicAuthenticationFilter2600
RequestCacheAwareFilter2700
SecurityContextHolderAwareRequestFilter2800
JaasApiIntegrationFilter2900
RememberMeAuthenticationFilter3000
AnonymousAuthenticationFilter3100
OAuth2AuthorizationCodeGrantFilter3200
SessionManagementFilter3300
ExceptionTranslationFilter3400
FilterSecurityInterceptor3500
AuthorizationFilter3600
SwitchUserFilter3700

4. 获取用户登录信息

如果不使用安全框架,可以通过HttpSession读写用户数据,Spring Security对HttpSession的用户信息进行了封装,开发者获取用户信息的方式有两种:

  • 从SecurityContextHoler中获取
  • 从当前请求对象中获取

4.1 从SecurityContextHoler中获取

@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;
    }
}

4.2 从当前请求对象中获取

@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);
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值