当前位置: 首页 > 科技观察

查看SpringSecurity系列登录详情

时间:2023-03-17 14:52:02 科技观察

上一篇讲了如何用更优雅的方式自定义SpringSecurity登录逻辑。更优雅的方式可以有效避免自定义过滤器带来的低效。推荐大家一定要阅读,也可以顺便了解一下SpringSecurity中的认证逻辑。在此基础上,本文将继续与大家探讨如何存储登录用户的详细信息。1.AuthenticationAuthentication这个接口之前跟大家讨论过很多次了,今天再说一遍。Authentication接口用来保存我们的登录用户信息。其实就是对principal(java.security.Principal)的进一步封装。我们看一个Authentication的定义:publicinterfaceAuthenticationextendsPrincipal,Serializable{CollectiongetAuthorities();ObjectgetCredentials();ObjectgetDetails();ObjectgetPrincipal();booleanisAuthenticated();接口voidsetAuthenticated(booleanisAuthenticated)throwsI中使用了getAuthorities方法获取用户的权限。getCredentials方法用于获取用户凭据,通常是密码。getDetails方法用于获取用户携带的详细信息,可能是当前请求之类的。getPrincipal方法用于获取当前用户,可以是用户名,也可以是用户对象。isAuthenticated当前用户是否认证成功。这里有一个更有趣的方法,叫做getDetails。关于该方法,源码解释如下:存储认证请求的附加信息。这些可能是IP地址,证书序列号等。从这个解释中我们可以看出,这个方法实际上是用来存储相关身份的其他认证信息,如IP地址,证书信息等。实际上,默认情况下,这里存放的是用户登录的IP地址和sessionId。我们从源码的角度来看。2.源码分析松哥的SpringSecurity系列已经写到第十二篇了。看了前面几篇文章,相信大家都明白了,用户登录时必须经过的一个过滤器就是UsernamePasswordAuthenticationFilter。在这个类的attemptAuthentication方法中,做Extraction,在attemptAuthentication方法中,会调用一个方法,就是setDetails。我们看一下setDetails方法:protectedvoidsetDetails(HttpServletRequestrequest,UsernamePasswordAuthenticationTokenauthRequest){authRequest.setDetails(authenticationDetailsS??ource.buildDetails(request));}UsernamePasswordAuthenticationToken是具体实现值的细节,所以这里是通过authenticationDetailsS??ource来构造的,我们看下:publicclassWebAuthenticationDetailsS??ourceimplementsAuthenticationDetailsS??ource{publicWebAuthenticationDetailsbuildDetails(HttpServletRequestcontext){returnnewWebAuthenticationDetails(context);}}publicclassWebAuthenticationDetailsimplementsSerializable{privatefinalStringremoteAddress;privatefinalStringsessionId;publicWebAuthenticationDetails(HttpServletRequestrequest){this.remoteAddress=request.getRemoteAddr();HttpSessionsession=request.getSession(false);this.sessionId=(session!=null)?session.getId():null;}//省略其他方法}默认使用WebAuthenticationDetailsS??ource构建WebAut亨蒂卡蒂onDetails,并将结果设置为Authentication的details属性。WebAuthenticationDetails中定义的属性,看一下基本就能明白,就是保存用户登录地址和sessionId。那么看到这里,大家基本就明白了,用户登录的IP地址其实是可以直接从WebAuthenticationDetails中获取到的。让我举一个简单的例子。比如我们登录成功后,可以通过以下方法随时随地获取用户IP:getDetails();System.out.println(details);}}之所以在服务中完成这个获取过程,是为了随时随地演示功能。然后我们在控制器中调用这个方法。访问接口时,可以看到如下日志:WebAuthenticationDetails@fffc7f0c:RemoteIpAddress:127.0.0.1;SessionId:303C7F254DF8B86667A2B20AA0667160可以看到给了用户的IP地址和SessionId。这两个属性在WebAuthenticationDetails中都有对应的get方法,也可以单独获取属性值。3、定制当然WebAuthenticationDetails也是可以定制的,因为它默认只提供IP和sessionid信息。如果我们想保存更多关于Http请求的信息,我们可以通过自定义WebAuthenticationDetails来实现。如果我们要自定义WebAuthenticationDetails,还需要和WebAuthenticationDetailsS??ource一起重新定义。结合上一篇的验证码登录,给大家看一个自定义WebAuthenticationDetails的例子。上篇文章我们是在MyAuthenticationProvider类中进行验证码判断的,回顾一下上篇文章的代码:publicclassMyAuthenticationProviderextendsDaoAuthenticationProvider{@OverrideprotectedvoidadditionalAuthenticationChecks(UserDetailsuserDetails,UsernamePasswordAuthenticationTokenauthentication)throwsAuthenticationException{HttpServletRequestreq=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();Stringcode=req.getParameter("code");Stringverify_code=(String)req.getSession().getAttribute("verify_code");if(code==null||verify_code==null||!code.equals(verify_code)){thrownewAuthenticationServiceException("验证码错误");}super.additionalAuthenticationChecks(userDetails,authentication);}}但是这个验证操作,我们也可以在自定义的WebAuthenticationDetails中进行,我们定义如下两个类:publicclassMyWebAuthenticationDetailsextendsWebAuthenticationDetails{privatebooleanisPassed;publicMyWebAuthenticationDetails(HttpServletRequestreq){super(req);Stringcode=req.getParameter("code");Stringverify_code=(String)req.getSession().getAttribute("verify_code");if(code!=null&&verify_code!=null&&code.equals(verify_code)){isPassed=true;}}publicbooleanisPassed(){returnisPassed;}}@ComponentpublicclassMyWebAuthenticationDetailsSourceimplementsAuthenticationDetailsSource{@OverridepublicMyWebAuthenticationDetailsbuildDetails(HttpServletRequestcontext){returnnewMyWebAuthenticationDetails(context);}}首先我们定义MyWebAuthenticationDetails,由于它的构造方法中,刚好就提供了HttpServletRequest对象,所以我们可以直接利用该对象进行Judgmentoftheverificationcode,andsavethejudgmentresulttotheisPassedvariable.Ifwewanttoexpandtheattributes,weonlyneedtodefinemoreattributesinMyWebAuthenticationDetails,andthenextractthemfromHttpServletRequestandsetthemtothecorrespondingattributes.Inthisway,aftertheloginissuccessfulAfterthat,theseattributescanbeobtainedanytimeandanywhere.Finally,constructMyWebAuthenticationDetailsinMyWebAuthenticationDetailsSourceandreturn.定义完成后,接下来,我们就可以直接在MyAuthenticationProvider中进行调用了:publicclassMyAuthenticationProviderextendsDaoAuthenticationProvider{@OverrideprotectedvoidadditionalAuthenticationChecks(UserDetailsuserDetails,UsernamePasswordAuthenticationTokenauthentication)throwsAuthenticationException{if(!((MyWebAuthenticationDetails)authentication.getDetails()).isPassed()){thrownewAuthenticationServiceException("验证码错误");}super.additionalAuthenticationChecks(userDetails,authentication);}}直接从认证中获取详情,调用isPassed方法。如果有问题,直接抛出异常即可。最后一个问题是如何将系统默认的WebAuthenticationDetailsS??ource替换为自定义的MyWebAuthenticationDetailsS??ource。这很简单。我们只需要在SecurityConfig中定义:@AutowiredMyWebAuthenticationDetailsS??ourcemyWebAuthenticationDetailsS??ource;@Overrideprotectedvoidconfigure(HttpSqueecurity.{Reuthhttp)throwsExcepize(http)and().formLogin().authenticationDetailsS??ource(myWebAuthenticationDetailsS??ource)...}将MyWebAuthenticationDetailsS??ource注入到SecurityConfig中,在formLogin中配置authenticationDetailsS??ource成功使用我们的自定义WebAuthenticationDetails。自定义完成后,WebAuthenticationDetails中原有的功能仍然保留,即我们可以继续使用老方法继续获取用户IP、sessionId等信息,如下:@ServicepublicclassHelloService{publicvoidhello(){Authenticationauthentication=SecurityContextHolder.getContext().getAuthentication();MyWebAuthenticationDetailsdetails=(MyWebAuthenticationDetails)authentication.getDetails();System.out.println(details);}}这里强制改type的时候,改成MyWebAuthenticationDetails即可。本文案例可从GitHub下载:https://github.com/lenve/spring-security-samples本文转载自微信公众号“江南一点雨”,你可以通过以下二维码关注。转载本文请联系江南一点鱼公众号。