欢迎来到 厦门市某某地暖科技培训学校
全国咨询热线:020-123456789
联系我们

地址:联系地址联系地址联系地址

电话:020-123456789

传真:020-123456789

邮箱:admin@aa.com

新闻中心
Spring Security认证器实现
  来源:厦门市某某地暖科技培训学校  更新时间:2024-05-07 05:47:33

Spring Security认证器实现

目录

  • 拦截请求
  • 验证过程
  • 返回完整的认证Authentication
  • 收尾工作
  • 结论

一些权限框架一般都包含认证器和决策器 ,前者处理登陆验证,器实后者处理访问资源的认证控制

Spring Security的登陆请求处理如图

Spring Security认证器实现

下面来分析一下是怎么实现认证器的

拦截请求

首先登陆请求会被UsernamePasswordAuthenticationFilter拦截 ,这个过滤器看名字就知道是器实一个拦截用户名密码的拦截器

主要的验证是在attemptAuthentication()方法里  ,他会去获取在请求中的认证用户名密码,并且创建一个该用户的器实上下文,然后在去执行一个验证过程

String username = this.obtainUsername(request);nString password = this.obtainPassword(request);n//创建上下文nUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,认证 password);nthis.setDetails(request, authRequest);nreturn this.getAuthenticationManager().authenticate(authRequest);n

可以看看UsernamePasswordAuthenticationToken这个类 ,他是器实继承了AbstractAuthenticationToken ,然后这个父类实现了Authentication

Spring Security认证器实现

由这个类的方法和属性可得知他就是存储用户验证信息的 ,认证器的器实主要功能应该就是验证完成后填充这个类

回到UsernamePasswordAuthenticationToken中 ,在上面创建的认证过程了可以发现

public UsernamePasswordAuthenticationToken(Object principal,Object credentials){ n super(null);n this.principal=principal;n this.credentials=credentials;n //还没认证n setAuthenticated(false);n}n

还有一个super(null)的处理,因为刚进来是器实还不知道有什么权限的,设置null是认证初始化一个空的权限

//权限利集合nprivate final Collection<GrantedAuthority> authorities;n//空的集合npublic static final List<GrantedAuthority> NO_AUTHORITIES = Collections.emptyList();n//初始化nif (authorities == null) { n this.authorities = AuthorityUtils.NO_AUTHORITIES;n return;n}n

那么后续认证完还会把权限设置尽量 ,此时可以看UsernamePasswordAuthenticationToken的器实另一个重载构造器

//认证完成npublic UsernamePasswordAuthenticationToken(Object principal, Object credentials,n Collection<? extends GrantedAuthority> authorities) { n super(authorities);n this.principal = principal;n this.credentials = credentials;n super.setAuthenticated(true); // must use super, as we overriden}n

在看源码的过程中,注释一直在强调这些上下文的认证填充和设置都应该是由AuthenticationManager或者AuthenticationProvider的实现类去操作

验证过程

接下来会把球踢给AuthenticationManager ,但他只是个接口

/**n * Attempts to authenticate the passed { @link Authentication} object, returning an * fully populated <code>Authentication</code> object (including granted authorities)n * if successful.n **/npublic interface AuthenticationManager { n Authentication authenticate(Authentication authentication)n throws AuthenticationException;n}n

注释也写的很清楚了 ,认证完成后会填充Authentication

接下来会委托给ProviderManager ,因为他实现了AuthenticationManager

刚进来看authenticate()方法会发现他先遍历了一个List<AuthenticationProvider>集合

/**n * Indicates a class can process a specific Authentication n **/npublic interface AuthenticationProvider { n Authentication authenticate(Authentication authentication)n throws AuthenticationException;n //支不支持特定类型的authenticationn boolean supports(Class<?> authentication);n}n

实现这个类就可以处理不同类型的Authentication ,比如上边的UsernamePasswordAuthenticationToken,对应的处理类是AbstractUserDetailsAuthenticationProvider ,为啥知道呢,因为在这个supports()里

public boolean supports(Class<?> authentication) { nttreturn (UsernamePasswordAuthenticationToken.classntttt.isAssignableFrom(authentication));n}n

注意到这个是抽象类,实际的处理方法是在他的子类DaoAuthenticationProvider里 ,但是最重要的authenticate()方法子类好像没有继承,看看父类是怎么实现这个方法的

  1. 首先是继续判断Authentication是不是特定的类
  2. Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));
  3. 查询根据用户名用户 ,这次就是到了子类的方法了 ,因为这个方法是抽象的
  4. user=retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
  5. 接着DaoAuthenticationProvider会调用真正实现查询用户的类UserDetailsService
  6. UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
  7. UserDetailsService这个类信息就不陌生了 ,我们一般都会去实现这个类来自定义查询用户的方式 ,查询完后会返回一个UserDetails,当然也可以继承这个类来扩展想要的字段 ,主要填充的是权限信息和密码
  8. 检验用户,如果获取到的UserDetails是null ,则抛异常,不为空则继续校验
  9. //检验用户合法性 preAuthenticationChecks.check(user); //校验密码 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
  10. 第一个教育是判断用户的合法性,就是判断UserDetails里的几个字段
  11. //账号是否过期 boolean isAccountNonExpired(); //账号被锁定或解锁状态 。 boolean isAccountNonLocked(); //密码是否过期 boolean isCredentialsNonExpired(); //是否启用 boolean isEnabled();
  12. 第二个则是由子类实现的,判断从数据库获取的密码和请求中的密码是否一致 ,因为用的登陆方式是根据用户名称登陆  ,所以有检验密码的步骤
  13. String presentedPassword = authentication.getCredentials().toString(); if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); }
  14. 需要主要的是请求中的密码是被加密过的,所以从数据库获取到的密码也应该是被加密的
  15. 注意到当完成校验的时候会把信息放入缓存
  16. //当没有从缓存中获取到值时,这个字段会被设置成false if (!cacheWasUsed) { this.userCache.putUserInCache(user); } //下次进来的时候回去获取 UserDetails user = this.userCache.getUserFromCache(username);
  17. 如果是从缓存中获取 ,也是会走检验逻辑的
  18. 最后完成检验,并填充一个完整的Authentication
  19. return createSuccessAuthentication(principalToReturn, authentication, user);

由上述流程来看,Security的检验过程还是比较清晰的,通过AuthenticationManager来委托给ProviderManager ,在通过具体的实现类来处理请求,在这个过程中,将查询用户的实现和验证代码分离开来

整个过程看着像是策略模式 ,后边将变化的部分抽离出来,实现解耦

返回完整的Authentication

前边提到的认证成功会调用createSuccessAuthentication()方法,里边的内容很简单

UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(n principal, authentication.getCredentials(),n authoritiesMapper.mapAuthorities(user.getAuthorities()));n result.setDetails(authentication.getDetails());n

public UsernamePasswordAuthenticationToken(Object principal, Object credentials,n Collection<? extends GrantedAuthority> authorities) { n super(authorities);n this.principal = principal;n this.credentials = credentials;n super.setAuthenticated(true); // must use super, as we overriden }n

这次往supe里放了权限集合 ,父类的处理是判断里边的权限有没有空的 ,没有则转换为只读集合

for (GrantedAuthority a : authorities) { n if (a == null) { n throw new IllegalArgumentException(n "Authorities collection cannot contain any null elements");n }n}nArrayList<GrantedAuthority> temp = new ArrayList<>(nauthorities.size());ntemp.addAll(authorities);nthis.authorities = Collections.unmodifiableList(temp);n

收尾工作

回到ProviderManager里的authenticate方法 ,当我们终于从

result = provider.authenticate(authentication);n

走出来时 ,后边还有什么操作

  1. 将返回的用户信息负责给当前的上下文

if (result != null) { n tcopyDetails(authentication, result);n tbreak;n }n

  1. 删除敏感信息
  2. ((CredentialsContainer) result).eraseCredentials();
  3. 这个过程会将一些字段设置为null ,可以实现eraseCredentials()方法来自定义需要删除的信息

最后返回到UsernamePasswordAuthenticationFilter中通过过滤

结论

这就是Spring Security实现认证的过程了

Spring Security认证器实现

通过实现自己的上下文Authentication和处理类AuthenticationProvider以及具体的查询用户的方法就可以自定义自己的登陆实现
具体可以看Spring Security自定义认证器

原文链接 :https://www.cnblogs.com/aruo/p/16306421.html#%E9%AA%8C%E8%AF%81%E8%BF%87%E7%A8%8B


友情链接仙4攻略图文大全 仙剑四千佛塔图文攻略王者荣耀新手攻略-操作界面篇华山侠客风云传全剧情全结局全任务流程攻略(人物创建+全支线人物流程+全宝箱收集+全女主攻略+全心法收集+DLC天王归来+碧血丹心DLC全攻略) (289)【最强蜗牛】万象迷谷单局百万分攻略三国志7ps特技攻略(图文)PS5最终幻想7重制版图文攻略 全流程剧情详细攻略逆转检事1攻略《弓箭传说》天赋居然能获得这么多提升,还不赶紧学习一下?我的世界生存模式进阶攻略_生存模式攻略psp最终幻想7攻略图文详解(核心危机详细玩法攻略及流程)伊苏8BOSS打法原创《刺客信条:大革命》全流程百分百关卡同步率流程图文攻略+神器收集+巴黎故事+系统详细教程+技能详解+支线任务 【游侠攻略组】剑侠情缘手游食谱大全 剑侠情缘手游食谱汇总《刺客信条:大革命》全流程百分百关卡同步率流程图文攻略+神器收集+巴黎故事+系统详细教程+技能详解+支线任务 【游侠攻略组】 序列四,记忆一,乞丐之王仙剑三全攻略 如何快速升级2022最新最强蜗牛供奉周攻略附:希域斗舞攻略攻略(办法)北欧女神 pap 攻略,psp北欧女神a结局的完全的攻略原来玩过一次简单的都没玩过伏魔战记3.9g攻略(伏魔战记3.9j攻略 弓箭手)仙剑4图文攻略大全(仙剑4全支线任务过关详解)光辉岁月 —— 英雄无敌三《生化危机8》全剧情流程图文攻略强推!过关成功率翻倍?手把手教你局内技能学习思路,大神原来是这样点技能的!马尼拉自助游刺客信条大革命谋杀之谜切开中间人怎么做 切开中间人任务流程攻略爬华山需要准备什么,爬华山有什么需要注意的,华山行前攻略古墓丽影游戏专区梦幻西游:70级难民五开养号攻略,投入少收益好【四川旅游攻略——玩遍四川详细攻略,赶紧收藏看起来吧】三国志10技能获得攻略(三国志10怎么获得技能)刺客信条大革命谜题解谜攻略(耶稣的遗产谜题答案解析)东京攻略~六本木艺术三角 蓝瓶咖啡《仙剑奇侠传1》攻略:仙剑1通关攻略鬼阴坛三国志7威力加强版修改器攻略三国志战略版爆仓7本攻略 三国志战略版屯田可以爆仓吗2048游戏通关攻略,2048游戏通关秘籍,轻松成为游戏大师!2024年双十一冰箱选购攻略与推荐!电冰箱选购要点与指南!想买电冰箱,只要看这一篇就够了!北京旅行攻略(总结+实践版),北京旅游景点、路线、住宿推荐,2023年12月最新更新【四川旅游攻略——玩遍四川详细攻略,赶紧收藏看起来吧】家庭熟女故事2rpg攻略,沙盒2第二个教程按住角色那个怎么过
联系我们

地址:联系地址联系地址联系地址

电话:020-123456789

传真:020-123456789

邮箱:admin@aa.com

0.2182

Copyright © 2024 Powered by 厦门市某某地暖科技培训学校   sitemap