幼儿饰品瑜伽美体用品微软
投稿投诉
微软创意
爱情通信
用品婚姻
爱好看病
美体软件
影音星座
瑜伽周边
星座办公
饰品塑形
搞笑减肥
幼儿两性
智家潮品

SpringSecurity认证流程分

  1。认证流程分析
  SpringSecurity中默认的一套登录流程是非常完善并且严谨的。但是项目需求非常多样化,很多时候,我们可能还需要对SpringSecinity登录流程进行定制,定制的前提是开发者先深刻理解SpringSecurity登录流程,然后在此基础之上,完成对登录流程的定制。本文将从头梳理SpringSecurity登录流程,并通过几个常见的登录定制案例,深刻地理解SpringSecurity登录流程。
  本章涉及的主要知识点有:登录流程分析。配置多个数据源。添加登录验证码。1。1登录流程分析
  要搞清楚SpringSecurity认证流程,我们得先认识与之相关的三个基本组件(Authentication对象在前面文章种己经做过介绍,这里不再赘述):AuthenticationManager、ProviderManager以及AuthenticationProvider,同时还要去了解接入认证功能的过滤器AbstractAuthenticationProcessingFilter,这四个类搞明白了,基本上认证流程也就清楚了,下面我们逐个分析一下。1。1。1AuthenticationManager
  从名称上可以看出,AuthenticationManager是一个认证管理器,它定义了SpringSecurity过滤器要如何执行认证操作。AuthenticationManager在认证成功后,会返回一个Authentication对象,这个Authentication对象会被设置到SecurityContextHolder中。如果开发者不想用SpringSecurity提供的一套认证机制,那么也可以自定义认证流程,认证成功后,手动将Authentication存入SecurityContextHolder中。publicinterfaceAuthenticationManager{Authenticationauthenticate(Authenticationvar1)throwsAuthenticationException;}
  从AuthenticationManager的源码中可以看到,AuthenticationManager对传入的Authentication对象进行身份认证,此时传入的Authentication参数只有用户名密码等简单的属性,如果认证成功,返回的Authentication的属性会得到完全填充,包括用户所具备的角色信息。AuthenticationManager是一个接口,它有着诸多的实现类,开发者也可以自定义AuthenticationManager的实现类,不过在实际应用中,我们使用最多的是ProviderManager,在SpringSecurity框架中,默认也是使用ProviderManager。1。1。2AuthenticationProvider
  SpringSecurity支持多种不同的认证方式,不同的认证方式对应不同的身份类型,AuthenticationProvider就是针对不同的身份类型执行具体的身份认证。例如,常见的DaoAuthenticationProvider用来支持用户名密码登录认证,RememberMeAuthenticationProvider用来支持记住我的认证。publicinterfaceAuthenticationProvider{Authenticationauthenticate(Authenticationvar1)throwsAuthenticationException;booleansupports(Classlt;?var1);}authenticate方法用来执行具体的身份忧证。supports方法用来判断当前AuthenticationProvider是否支持对应的身份类型。
  当使用用户名密码的方式登录时,对应的AuthenticationProvider实现类是DaoAuthenticationProvider,而DaoAuthenticationProvider继承自AbstractUserDetailsAuthenticationProvider并且没有重写authenticate方法,所以具体的认证逻辑在AbstractUserDetailsAuthenticationProvider的authenticate方法中。我们就从AbstractUserDetailsAuthenticationProvider开始看起:
  查看代码一开始先声明一个用户缓存对象userCache,默认情况下没有启用缓存对象。hideUserNotFoundExceptions表示是否隐藏用户名查找失败的异常,默认为true,为了确保系统安全,用户在登录失败时只会给出一个模糊提示,例如用户名或密码输入错误在SpringSecurity内部,如果用户名查找失败,则会抛出UsernameNotFoundException异常,但是该异常会被自动隐藏,转而通过一个BadCredentialsException异常来代替它,这样,开发者在处理登录失败异常时,无论是用户名输入错误还是密码输入错误,收到的总是BadCredentialsException,这样做的一个好处是可以避免新手程序员将用户名输入错误和密码输入错误两个异常分开提示。forcePrincipalAsString表示是否强制将Principal对象当成字符串来处理,默认是falser,Authentication中的Principal属性类型是一个Object,正常来说,通过Principal属性可以获取到当前登录用户对象(即UserDetails),但是如果forcePrincipalAsString设置为true,则Authentication中的Principal属性返回就是当前登录用户名,而不是用户对象。preAuthenticationChecks对象则是用于做用户状态检査,在用户认证过程中,需要检验用户状态是否正常,例如账户是否被锁定、账户是否可用、账户是否过期等。postAuthenticationChecks对象主要负责在密码校验成功后,检査密码是否过期。additionalAuthenticationChecks是一个抽象方法,主要就是校验密码,具体的实现在DaoAuthenticationProvider中。authenticate方法就是核心的校验方法了。在方法中,首先从登录数据中获取用户名,然后根据用户名去缓存中查询用户对象,如果査询不到,则根据用户名调用retrieveUser方法从数据库中加载用户;如果没有加载到用户,则抛出异常(用户不存在异常会被隐藏)。拿到用户对象之后,首先调用check方法进行用户状态检査,然后调用additionalAuthenticationChecks方法进行密码的校验操作,最后调用postAuthenticationChecks。check方法检査密码是否过期,当所有步骤都顺利完成后,调用createSuccessAuthentication方法创建一个认证后的UsernamePasswordAuthenticationToken对象并返回,认证后的对象中包含了认证主体、凭证以及角色等信息。
  这就是AbstractUserDetailsAuthenticationProvider类的工作流程,有几个抽象方法是在DaoAuthenticationProvider中实现的,我们再来看一下DaoAuthenticationProvider中的定义:
  查看代码首先定义了USERNOTFOUNDPASSWORD常量,这个是当用户查找失败时的默认密码;passwordEncoder是一个密码加密和比对工具,这个在后面会有专门的介绍,这里先不做过多解释;userNotFoundEncodedPassword变量则用来保存默认密码加密后的值;userDetailsService是一个用户查找工具,userDetailsService在前面己经讲过,这里不再赘述;userDetailsPasswordService则用来提供密码修改服务。在DaoAuthenticationProvider的构造方法中,默认就会指定PasswordEncoder,当然开发者也可以通过set方法自定义PasswordEncoder。additionalAuthenticationchecks方法主要进行密码校验,该方法第一个参数userDetails是从数据库中查询出来的用户对象,第二个参数authentication则是登录用户输入的参数。从这两个参数中分别提取出来用户密码,然后调用passwordEncoder。matches方法进行密码比对。retrieveUser方法则是获取用户对象的方法,具体做法就是调用UserDetailsServiceloadUserByUsername方法去数据库中查询。在retrieveUser方法中,有一个值得关注的地方。在该方法一开始,首先会调用prepareTimingAttackProtection方法,该方法的作用是使用PasswordEncoder对常量USERNOTFOUNDPASSWORD进行加密,将加密结果保存在userNotFoundEncodedPassword变量中。当根据用户名查找用户时,如果抛出了UsernameNotFoundException异常,则调用mitigateAgainstTimingAttack方法进行密码比对由有读者会说,用户都没查找到,怎么比对密码?需要注意,在调用mitigateAgainstTimingAttack方法进行密码比对时,使用了userNotFoundEncodedPassword变量作为默认密码和登录请求传来的用户密码进行比对,这是一个一开始就注定要失败的密码比对,那么为什么还要进行比对呢?这主要是为了避免旁道攻击(Sidechannelattack)。如果根据用户名査找用户失败,就直接抛出异常而不进行密码比对,那么黑客经过大量的测试,就会发现有的请求耗费时间明显小于其他请求,那么进而可以得出该请求的用户名是一个不存在的用户名(因为用户名不存在,所以不需要密码比对,进而节省时间),这样就可以获取到系统信息。为了避免这一问题,所以当用户查找失败时,也会调用mitigateAgainstTimingAttack方法进行密码比对,这样就可以迷惑黑客。createSuccessAuthentication方法则是在登录成功后,创建一个全新的UsernamePasswordAuthenticationToken对象,同时会判断是否需要进行密码升级,如果需要进行密码升级,就会在该方法中进行加密方案升級。通过对AbstractUserDetailsAuthenticationProvider和DaoAuthenticationProvider的讲解,相信你己经很明白AuthenticationProvider中的认证逻辑了。
  在密码学中,旁道攻击(Sidechannelattack)又称侧信道攻击、边信道攻击。这种攻击方式不是暴力破解或者是研究加密算法的弱点。它是基于从密码系统的物理实现中获取信息,比如时间、功率消耗、电磁泄漏等,这些信息可被利用于对系统的进一步破解。1。1。3ProviderManager
  ProviderManager是AuthenticationManager的一个重要实现类,正在开始学习之前,我们先通过一幅图来了解一下ProviderManager和AuthenticationProvider之间的关系,如图31所示。
  图31
  在SpringSecurity中,由于系统可能同时支持多种不同的认证方式,例如同时支持用户名密码认证、RememberMe认证、手机号码动态认证等,而不同的认证方式对应了不同的AuthenticationProvider,所以一个完整的认证流程可能由多个AuthenticationProvider来提供。
  多个AuthenticationProvider将组成一个列表,这个列表将由ProviderManager代理。换句话说,在ProviderManager中存在一个AuthenticationProvider列表,在ProviderManager中遍历列表中的每一个AuthenticationProvider去执行身份认证,最终得到认证结果。
  ProviderManager本身也可以再配置一个AuthenticationManager作为parent,这样当ProviderManager认证失败之后,就可以进入到parent中再次进行认证。理论上来说,ProviderManager的parent可以是任意类型的AuthenticationManager,但是通常都是由ProviderManager来扮演parent的角色,也就是ProviderManager是ProviderManager的parent。
  ProviderManager本身也可以有多个,多个ProviderManager共用同一个parent,当存在多个过滤器链的时候非常有用。当存在多个过滤器链时,不同的路径可能对应不同的认证方式,但是不同路径可能又会同时存在一些共有的认证方式,这些共有的认证方式可以在parent中统一处理。
  根据上面的介绍,图32是ProviderManager和AuthenticationProvider关系图。
  图32
  我们重点看一下ProviderManager中的authenticate方法:
  查看代码
  这段源码的逻辑还是非常清晰的,我们分析一下:首先获取authentication对象的类型。分别定义当前认证过程抛出的异常、parent中认证时抛出的异常、当前认证结果以及parent中认证结果对应的变量。getProviders方法用来获取当前ProviderManager所代理的所有AuthenticationProvider对象,遍历这些AuthenticationProvider对象进行身份认证。判断当前AuthenticationProvider是否支持当前Authentication对象,要是不支持,则继续处理列表中的下一个AuthenticationProvider对象调用provider。authenticate方法进行身份认证,如果认证成功,返回认证后的Authentication对象,同时调用copyDetails方法给Authentication对象的details属性赋值,由于可能是多个AuthenticationProvider执行认证操作,所以如果抛出异常,则通过lastException变量来记录。在for循环执行完成后,如果result还是没有值,说明所有的AuthenticationProvider都认证失败,此时如果parent不为空,则调用parent的authenticate方法进行认证。接下来,如果result不为空,就将result中的凭证擦除,防止泄漏,如果使用了用户名密码的方式登录,那么所谓的擦除实际上就是将密码字段设置为null,同时将登录成功的事件发布出去(发布登录成功事件需要parentResult为null。如果parentResult不为null,表示在parent中已经认证成功了,认证成功的事件也己经在parent中发布出去了,这样会导致认证成功的事件重复发布)。如果用户认证成功,此时就将result返回,后面的代码也就不再执行了。如果前面没能返回result,说明认证失败。如果lastException为null,说明parent为null或者没有认证亦或者认证失败了但是没有抛出异常,此时构造ProviderNotFoundException异常赋值给lastException。如果parentException为null,发布认证失败事件(如果parentException不为null,则说明认证失败事件已经发布过了)。最后抛出lastException异常。
  这就是ProviderManager中authenticate方法的身份认证逻辑,其他方法的源码要相对简单很多,在这里就不一一解释了,
  现在,大家已经熟悉了Authentication、AuthenticationManager、AuthenticationProvider以及ProviderManager的工作原理了,接下来的问题就是这些组件如何跟登录关联起来?这就涉及一个重要的过滤器AbstractAuthenticationProcessingFilter。1。1。4AbstractAuthenticationProcessingFilter
  作为SpringSecurity过滤器链中的一环,AbstractAuthenticationProcessingFilter可以用来处理任何提交给它的身份认证,图33描述了AbstractAuthenticationProcessingFilter的工作流程:
  图33
  图中显示的流程是一个通用的架构。
  AbstractAuthenticationProcessingFilter作为一个抽象类,如果使用用户名密码的方式登录,那么它对应的实现类是UsernamePasswordAuthenticationFilter;构造出来的Authentication对象则是UsernamePasswordAuthenticationToken。至于AuthenticationManager,前面已经说过,一般情况下它的实现类就是ProviderManager,这里在ProviderManager中进行认证,认证成功就会进入认证成功的回调,否则进入认证失败的回调。因此,我们可以对上面的流程图再做进一步细化,如图34所示。
  图34
  前面第2章中所涉及的认证流程基本上就是这样,我们来大致梳理一下:当用户提交登录请求时,UsernamePasswordAuthenticationFilter会从当前请求HttpServletRequest中提取出登录用户名密码,然后创建出一个UsernamePasswordAuthenticationToken对象。UsernamePasswordAuthenticationToken对象将被传入ProviderManager中进行具体的认证操作。如果认证失败,则SecurityContextHolder中相关信息将被清除,登录失败回调也会被调用,如果认证成功,则会进行登录信息存储、Session并发处理、登录成功事件发布以及登录成功方法回调等操作。
  这是认证的一个大致流程。接下来我们结合AbstractAuthenticationProcessingFilter和UsernamePasswordAuthenticationFilter的源码来看一下。
  先来看AbstractAuthenticationProcessingFilter源码(部分核心代码):
  查看代码首先通过requiresAuthentication方法来判断当前请求是不是登录认证请求,如果是认证请求,就执行接下来的认证代码;如果不是认证请求,则直接继续走剩余的过滤器即可。调用attemptAuthentication方法来获取一个经过认证后的Authentication对象,attemptAuthentication方法是一个抽象方法,具体实现在它的子类UsernamePasswordAuthenticationFilter中。认证成功后,通过sessionStrategy。onAuthentication方法来处理session并发问题。continueChainBeforeSuccessfulAuthentication变量用来判断请求是否还需要继续向下走,默认情况下该参数的值为false,即认证成功后,后续的过滤器将不再执行了。unsuccessfulAuthentication方法用来处理认证失败事宜,主要做了三件事:从SecurityContextHolder中清除数据;清除Cookie等信息;调用认证失败的回调方法。successfulAuthentication方法主要用来处理认证成功事宜,主要做了四件事:向SecurityContextHolder中存入用户信息;处理Cookie;发布认证成功事件,这个事件类型InteractiveAuthenticationSuccessEvent,表示通过一些自动交互的方式认证成功,例如通过RememberMe的方式登录;调用认证成功的回调方法。
  这就是AbstractAuthenticationProcessingFilter大致上所做的事情,还有一个抽象方法attemptAuthentication是在它的继承类UsernamePasswordAuthenticationFilter中实现的,接下来我们来看下UsernamePasswordAuthenticationFilter类:
  查看代码首先声明了默认情况下登录表单的用户名字段和密码字段,用户名字段的key默认是username,密码字段的key默认是password。当然,这两个字段也可以自定义,自定义的方式就是我们在SecurityConfig中配置的。usernameParameter(uname)和。passwordParameter(passwd)(参考前几节)在UsernamePasswordAuthenticationFilter过滤器构建的时候,指定了当前过滤器只用来处理登录请求,默认的登录请求是login,当然开发者也可以自行配置。接下来就是最重要的attemptAuthentication方法了,在该方法中,首先确认请求是post类型;然后通过obtainUsername和obtainPassword方法分别从请求中提取出用户名和密码,具体的提取过程就是调用request。getParameter方法;拿到登录请求传来的用户名密码之后,构造出一个authRequest,然后调用getAuthenticationManager()。authenticate方法进行认证,这就进入到我们前面所说的ProviderManager的流程中了,具体认证过程就不再赘述了。
  以上就是整个认证流程。
  搞懂了认证流程,那么接下来如果想要自定义一些认证方式,就会非常容易了,比如定义多个数据源、添加登录校验码等。下面,我们将通过两个案例,来活学活用上面所讲的认证流程。

深汕特别合作区首个村企合作产业园签约3月14日,深圳市深汕特别合作区蛟湖产业园合作协议签约仪式举行。深汕城投公司与鹅埠镇蛟湖村民小组、村委会签订合作协议,以政府监管、企业运作、村集体合作模式,推进蛟湖村留用地开发……泰国假期专车909号列车带游客打卡北碧府桂河大桥近日,据泰媒报道称,泰国曼谷华南蓬火车站在每个周末及公共假期设有一趟909号列车,途径佛统府并前往北碧府知名的桂河大桥。据了解,该座大桥建于第二次世界大战期间,战争结束后泰国政……全国餐饮市场盈利超4万亿破产商家我就贡献了好几万餐饮消费市场加快恢复向好,可以开餐馆赚钱了?可以投资入股餐饮行业了?可以出去大吃大喝了?我看目前餐饮行业还是充满了危机,还是要小心谨慎,避免辛辛苦苦攒下了血汗钱付之东流,欲哭无……在太阳系近邻中寻找宜居行星受到中国科学界关注新华社北京4月14日电(记者喻菲、王晨曦)中国科幻大片《流浪地球》中人类飞往的比邻星,在科学家眼中并非生命生存的理想之地。中国科学家提出一个独特的原创性探测方案近邻宜居行星巡天……校长说观察细一点思考细一点沟通细一点,教育是细的艺术校长名片:涂晶,湖北省楚天技能名师,武汉市优秀班主任,黄陂区十大名优校园长。天下大事必做于细。教育是慢的艺术,更是细的艺术。观察细一点、思考细一点、沟通细一点我常常……华为员工越权访问机密数据被判刑断送了大好前途大可不必华为可以说是每一个打工人向往的地方。很多人挤破头都进不去,近日一位华为员工易某的操作,却让网友大跌眼镜。据财经网报道:2010年12月,易某从华为公司线缆物控部调任后,未……无间牧溪鹤怎么也没想到,拿下闪官,会是自己入局的开始《无间》是靳东自《伪装者》之后,再演谍战男主的一部新作,该剧由他与王丽坤、张志坚、奇道、王志文等人领衔,因为超高的人气和精彩的设计,一经播出便收视拉满。《无间》牧溪鹤怎么……4月之后,建议给孩子吃这菜,简单省事,营养全面,别心疼钱!四月一到,天气变化非常大,月初还一直下暴雨和冰雹,到了四月中旬以后,雨水开始减少,温度有所回升,我们终于感到了春意浓浓,让人一整天都有精神,胃口也越来越好。由于我们家老公很忙,……一个普通宝鸡人的淄博之问淄博火了,因为一份烧烤。奇怪吗?似乎有点奇怪,又似乎一点也不奇怪。奇怪的是这么一个国家历史文化名城、一个齐文化发祥地、一个GDP4000多亿的中国优秀旅游城市,怎么……我很高兴孙兴民离开孔蒂!主教练安东尼奥孔蒂的离开对孙兴民来说可能是幸运的。孔蒂在与南安普顿的比赛后被解雇。有争议的面试是个问题,但没有起色的表现和没完没了的糟糕表现才是更大的因素。直到上赛季,他……熄灯号腊梅点点晨起在花园里散步,突然发现腊梅又多开了许多,在晨雾中越发显得动人。妻看了笑着说:这腊梅可是越冷开得越艳呀!好一个越冷开得越艳!对腊梅,我有一种与生俱来、不可释解的情愫。倒不是说……保定这项数据增速超一倍!4月18日,保定市召开开展气候投融资试点助推保定市绿色低碳高质量发展新闻发布会。新闻发布会现场记者从会上了解到,气候投融资因应对气候变化而产生,因碳达峰碳中和而发展……
11月金融数据值得关注的四大信号1、M2和M1明显背离11月M2增速高达12。4,但M1却不增反降下行到了4。6,M1和M2的增速明显背离。为什么会这样呢?我们认为主要原因是疫情导致预防性储……你穿什么颜色最显白?这几种颜色穿对了,效果翻倍想要在穿搭上取胜,让别人眼前一亮的感觉,那就看看你是否穿对了颜色,你知道你穿什么颜色最显白吗?第一:了解自己肤色我们中国人的皮肤都是以黄色为基调,只不过有些人是冷白……李宇春,一个很女人的女人有人说,人生是一部不断与自己战斗的电视剧。李宇春的人生,就是这句话的真实写照。近日,她的病情引起广泛关注,严重的时候,只能坐在轮椅上。宇春爸还专门出来安慰,让大家不……非洲羚羊哪里起源?中国这件千万年前化石揭开谜团邓氏临夏羚的头骨和颈椎化石标本。中科院古脊椎所供图中新网北京11月3日电(记者孙自法)现代非洲声名远扬的萨瓦纳动物群及当中极具代表性的各种羚羊,它们在非洲的化石记录一般不……马化腾内部讲话透露出了什么信号腾讯每年都会举行年度员工大会,在年度员工大会上,马化腾都会对过去年的公司的整体运行情况做一个总结以及对未来的展望。在以往的历次讲话中,马化腾的讲话都一般比较温和与客气。这……客观评价vivos16pro,它有两个优点和三个不足这两天,vivo发布了它的年度中端机型16系列,笔者第一时间对照详细的参数配置作了一番研究和对比,因为篇幅有限,也考虑到写作的专注要求,今日本文只客观评价vivos16pro这……欧盟官方通过决议将加大对游戏产业的扶持力度近日,欧洲议会以560票支持,34票反对,16票弃权的的巨大优势通过了一项有关于电子竞技的决议。在接下来的几个月里,欧洲立法者将会按照议会的说法采取行动,以支持欧洲电子竞……美国胁迫欧洲阻挠中国统一,威胁若台海生战,每年损失达2。5万据英国《金融时报》报道,美国为了裹挟欧洲,干预中国台海问题,最近通过美国国务院向欧洲国家共享了一份数据,这数据是一家美国研究公司提供的,主要内容是说如果台海地区因为一旦发生冲突……老厂区变身创业基地,这里正青春新华社重庆11月3日电题:老厂区变身创业基地,这里正青春新华社记者陈青冰、李晓婷位于重庆市渝中区的枇杷山后街,隐匿于几条主路之间,在狭窄陡峭的山城地形中,如同一幅在……突破卡脖子,填补国内空白!他立志中国制造,成果出口多国!他以破冰前行的决心,担当先锋,突破国内智能研发卡脖子工程,设计出国内首套皇冠瓶盖高速视觉检测系统线,填补国内空白;他以心无旁骛的定力做强产业,坚守归国人才的初心与担当,研发系列……32岁沈女士为矫正歪鼻从国外到北京,术后感叹回国决定做对了求美者自述:3前,远在泰国的我,因为笃信异国的审美,想把鼻子垫高点,结果却变成了我的灾难。当时我做的是鼻骨内推鼻尖塑形,当时感觉还不错,鼻梁确实又挺又翘,正当我陶醉……盈利能力下滑房地产贷款占比超红线,九江银行不如意的上半年编辑导语:如今的九江银行可谓焦头烂额。图百度百科九江银行股份有限公司(下称九江银行)于近日披露了不如意的半年报。数据显示,今年上半年九江银行实现经营收入52。……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网