开发者学堂课程【Spring Security知识精讲与实战演示(一):spring Security认证流程分析】学习笔记与课程紧密联系,让用户快速学习知识
课程地址:https://developer.aliyun.com/learning/course/730/detail/13037
spring Security 认证流程分析
认证的底层流程。认证已经做完,为什么需要梳理流程。需要注意的一个问题是,当我们点开配置文件,里面认证用的用户名和密码写到了内存中。在实际开发中,认证是不可以直接把用名和密码写到内存中的。应该用自己数据库中的用户名和密码,当要用数据库中的用户名和密码,login.jsp中的login并不是我们写的,别人提供的认证处理器不知道我们的用户,不知道里面的内容。所以当想用它的处理器,又想用我们自己的数据时,可以分析一下流程,找到处理器里边真正处理登陆操作,即认证操作的业务逻辑,然后再进行梳理业务逻辑。
它如何写,我们也写一套类似的,让它用我们自己写的那套,流程就可以走通了。此时,我们分析一下 Spring Security 的认证流程,UsernamePasswordAuthenticationFilterI,这是认证的一个过滤器,真正的认证业务是靠这个过滤器去完成的。
当我们想了解它的认证业就是通过这个过滤器知道的。此时我们可以把所有东西都关掉,把 UsernamePasswordAuthenticationFilterI 粘贴复制点进去,点进来之后,可以看见我们的用户名必须叫 username,密码属性名叫 password,不可以用别的用户名和密码,否则它是不认识的。它规定了我们的处理器请求路径是 login,请求方式是 POST,否则也是不可以的。若我们的请求方式不是 POST,它会显示错误。
往下看,username 和 password 需要先提出来,request 是 HttpServletRequest中的request,从中 request.getMethod()出来username,同理可得,request.getMethod() 出来password。String username = this. obtainUsername(request);String password = this. obtainPassword(request)这两种是非常简单的。
在username=""中,写的是空值,如果是nu11值,它会空速不转,为了防止后续出现空指针。username=username.trin()中,username.后是空格,注意的是前后空格去掉,但是密码不去除,密码可能会加空格,也可以限制我们不加空格,也可以设置只用哪些字符,但是如果没有说明,可以填写空格。
用户名和密码在下方,在用户名前面又封装了UsernamePasswordAuthenticationToken,点击UsernamePasswordAuthenticationToken,就会出现如上图所示,两种构造方法名字一样,参数不一样。
第一种有 Object principal, Object credentials 这两个参数,第二种有Object principal, Object credentials, Collection<? extends GrantedAuthority> authori super(authorities) 这三种参数。
在第一种参数中,Object principal 是用户名,credentials 是密码,在以后做认证的时候需要注意的是,有可能会报错,用户名写错就会显示 Object principal 这个单词写错了,密码写错就会显示 credentials 这个单词写错。所以要对这两个单词眼熟。在下边 Collection 中不一定要赋值,可以看见它显示的是错误。在第二种中赋值,显示的就是 true。
所以综上,需要我们注意的是在认证前,它走的是第一种,认证错误,走的是第二种。换句话说,认证通过后,不给权限会显示认证不通过。
在最后一步 return this.getAuthenticationMnager().authenticate(authRequest);中认证最有逻辑,点击 authenticate,进入之后我们会发现,Authentication authenticate(Authentication varl) throws AuthenticationException 是个接口,点击ctrl h看到所有的实验类,所有带in的可以不用担心,在 ProviderManager.class中找到authenticate这个方法,在这个方法中,var8.hasNext的出现使得这个方法便利。在这个方法中,点击 getProviders,进入之后,我们会发现出现Lis<AuthenticationProvider>这个集合,它把每一种论证方式封装了一个叫AuthenticationProvidex 的对象,论证就是登录,就是查询验证密码。AuthenticationProvidex 是非常强大的,它也可以在第三方登录,例如微信登录,qq登录。如果是第三方登录,它的登录流程是不一样的,它会自动去判断我们是哪一种登录,然后选择对应的验证方式。
在 var8.hasNext 遍历中,找到 authenticate 这个方法名,和上述的方法名是相同的。点进去后,点击 ctrl h 进入,找到AbstractUserDetailsAuthenticationProvider,因为此时的认证流程并不是它的分支,所以它不能使用 Debug,它也进入不了此时的流程,这需要提前把我们的认证做完才可以进入。
此时不想要去认证,所以它走的是 AbstractUserDetailsAuthenticationProvider,需要注意的是用的是 ProviderManager.class 里的 authenticate。在AbstractUserDetailsAuthenticationProvider.class 中,找到 authenticate 这个方法,下边是它的认证过程。在正常流程中,retrieveUser在做认证,UsernamePasswordAuthenticationToken 是用户名和密码。点击 retrieveUser,进入之后出现 protected abstract UserDetails retrieveUser(Stringvarl,UsernamePasswordAuthenticationToken var2) throw AuthenticationExee 内幕,但是并没有实现,点击 ctrl h,出现DaoAuthenticationProvider 并点击,找到 retrieveUser,此时才是真正认证的业物。
UserDetails loadedUser = this. getUserDetailsService(). loadUserByUsername(username) 中的 UserDetails 是 Spring Security 内幕的用户对象。在我们自己的数据库有一张用户表,它对应的是我们自己的用户对象,但是我们自己的用户对象 Spring Security 是不知道的,它自己有一个用户对象,此时,这个用户对象我们想要得到,需要用用户名登录密码进行查询。
同理,它也是一样,不同的是比较复杂。它走的是 loadUserByllsername,点进去,出现 UserDetailsService,还出现 UserDetails loadUserByUsername(String varl) throws UsernameNotFoundException 接口需要我们去实验,UserDetails 是Spring Security 自己提供的用户对象,如果想使用自己的数据库的信息进行论证,我们就必须使用 UserDetailsService 接口,在接口内部根据里面的流程进行认证。我们已找到自己的数据库信息与 Spring Security 做衔接,有了 UserDetails loadUserByUsername(String varl) throws
UsernameNotFoundException接口方便我们做后续的工作。



