• spring security 认证简介


    1. 简介

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

    2.认证核心组件

    2.1 Authentication

    当用户登录后,都会对应一个不同的Authentication实例,最常用的实现类是UsernamePasswordAuthenticationToken和RememberMeAuthenticationToken

    1. public interface Authentication extends Principal, Serializable {
    2. //获取用户权限列表
    3. Collectionextends GrantedAuthority> getAuthorities();
    4. //获取用户凭证,一般来说就是密码,认证成功后密码一般会被删除
    5. Object getCredentials();
    6. //获取用户的详细信息,可能是当前请求之类等
    7. Object getDetails();
    8. //获取当前用户,例如一个用户名或者一个用户对象
    9. Object getPrincipal();
    10. boolean isAuthenticated();
    11. void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
    12. }

    2.2 AuthenticationManager

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

    1. public interface AuthenticationManager {
    2. Authentication authenticate(Authentication authentication) throws AuthenticationException;
    3. }

    2.3 ProviderManager

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

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

    1. //省略部分代码
    2. public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
    3. private AuthenticationEventPublisher eventPublisher;
    4. private List providers;
    5. private AuthenticationManager parent;
    6. private boolean eraseCredentialsAfterAuthentication;
    7. public ProviderManager(AuthenticationProvider... providers) {
    8. this(Arrays.asList(providers), (AuthenticationManager)null);
    9. }
    10. public ProviderManager(List providers) {
    11. this(providers, (AuthenticationManager)null);
    12. }
    13. public ProviderManager(List providers, AuthenticationManager parent) {
    14. }
    15. //核心部分,省略部分代码
    16. public Authentication authenticate(Authentication authentication) throws AuthenticationExceptio {
    17. Classextends Authentication> toTest = authentication.getClass();
    18. AuthenticationException lastException = null;
    19. AuthenticationException parentException = null;
    20. Authentication result = null;
    21. Authentication parentResult = null;
    22. int currentPosition = 0;
    23. int size = this.providers.size();
    24. for (AuthenticationProvider provider : getProviders()) {
    25. result = provider.authenticate(authentication);
    26. if (result != null) {
    27. copyDetails(authentication, result);
    28. break;
    29. }
    30. }
    31. //如果providers都不能处理,由parent处理
    32. if (result == null && this.parent != null) {
    33. // Allow the parent to try.
    34. try {
    35. parentResult = this.parent.authenticate(authentication);
    36. result = parentResult;
    37. }
    38. catch (AuthenticationException ex) {
    39. parentException = ex;
    40. lastException = ex;
    41. }
    42. }
    43. if (result != null) {
    44. if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
    45. //擦除密码,发布事件
    46. ((CredentialsContainer) result).eraseCredentials();
    47. }
    48. if (parentResult == null) {
    49. this.eventPublisher.publishAuthenticationSuccess(result);
    50. }
    51. return result;
    52. }
    53. }
    54. }

    2.4 AuthenticationProvider

    AuthenticationProvider有点类似AuthenticationManager,但是多了一个supports方法用来判断是否支持给定的Authentication类型
    1. public interface AuthenticationProvider {
    2. Authentication authenticate(Authentication authentication) throws AuthenticationException;
    3. boolean supports(Class authentication);
    4. }

    2.5 UserDetails

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

    1. public interface UserDetails extends Serializable {
    2. //返回当前账户具备的权限
    3. Collectionextends GrantedAuthority> getAuthorities();
    4. String getPassword();
    5. String getUsername();
    6. boolean isAccountNonExpired();
    7. boolean isAccountNonLocked();
    8. boolean isCredentialsNonExpired();
    9. boolean isEnabled();
    10. }

     2.6 UserDetailsService

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

    1. public interface UserDetailsService {
    2. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
    3. }

    3. Filter

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

    3.1 默认的Filter顺序

    Filter用途
    ChannelProcessingFilter用于通道处理的
    WebAsyncManagerIntegrationFilter异步集成
    CorsFilter跨域资源共享
    CsrfFilter处理跨域站点伪造
    LogoutFilter注销
    CorsFilter跨域资源共享
    OAuth2AuthorizationRequestRedirectFilterOAuth2认证请求重定向
    X509AuthenticationFilterX509证书认证
    UsernamePasswordAuthenticationFilter用户名密码认证
    ConcurrentSessionFilter并发会话
    BearerTokenAuthenticationFilterBearer令牌认证
    RememberMeAuthenticationFilter记住我认证
    AnonymousAuthenticationFilter匿名认证
    ExceptionTranslationFilter异常转移
    SwitchUserFilter切换用户

    4. 获取用户登录信息

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

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

    4.1 从SecurityContextHoler中获取

    1. @Controller
    2. public class HelloController {
    3. @RequestMapping(method = RequestMethod.GET, path = "principal")
    4. @ResponseBody
    5. public Object principal() {
    6. SecurityContext context = SecurityContextHolder.getContext();
    7. Authentication authentication = context.getAuthentication();
    8. Object principal = authentication.getPrincipal();
    9. return principal;
    10. }
    11. }

    4.2 从当前请求对象中获取

    1. @RequestMapping("/authentication")
    2. public void authentication(Authentication authentication) {
    3. System.out.println("authentication = " + authentication);
    4. }
    5. @RequestMapping("/principal")
    6. public void principal(Principal principal, HttpServletRequest req) {
    7. System.out.println("req.getClass() = " + req.getClass());
    8. System.out.println("principal = " + principal);
    9. }
    10. @RequestMapping("/info")
    11. public void info(HttpServletRequest req) {
    12. String remoteUser = req.getRemoteUser();
    13. Authentication auth = ((Authentication) req.getUserPrincipal());
    14. boolean admin = req.isUserInRole("admin");
    15. System.out.println("remoteUser = " + remoteUser);
    16. System.out.println("auth.getName() = " + auth.getName());
    17. System.out.println("admin = " + admin);
    18. }

  • 相关阅读:
    IDEA设置背景为自定义照片
    rt-thread + lvgl :线程调用lv_task_handler() 之后,无法休眠
    【已解决】MySQL:执行sql查询出错误数据(MySQL隐藏机制-类型转换导致)
    日化用品行业集团采购管理系统改变传统采购模式,降低采购成本
    UVA 10405【LCS】【背包】
    for循环里的定时器引发的思考
    kubernetes-Service详解
    蓝牙基带的基础
    适用于 Windows 10 和 Windows 11 设备的笔记本电脑管理软件
    算法练习题(涉外黄成老师)
  • 原文地址:https://blog.csdn.net/qq_39385118/article/details/126154722