环境:SpringBoot2.2.11.RELEASE+JPA2Security流程处理Security的核心是Filter。Security执行过程的详细步骤如下图所示:1.1UsernamePasswordAuthenticationFilter的父类是AbstractAuthenticationProcessingFilter,首先执行父类中的doFilter方法。1.2执行UsernamePasswordAuthenticationFilter中的attemptAuthentication方法这里实例化了UsernamePasswordAuthenticationToken对象,保存在用户名和密码中,用于后续验证1.3进入验证this.getAuthenticationManager().authenticate(authRequest)这里使用系统提供的ProviderManager对象进行确认。关键如下这里的for循环首先判断是否支持AuthenticationProviderClasstoTest=authentication.getClass();这里的toTest是UsernamePasswordAuthenticationFilter类中调用的如下对象1.4既然我们要验证用户名和密码,就必须提供一个AuthenticationProvider对象,同时还必须支持UsernamePasswordAuthenticationToken对象类型。所以我们提供了下面的DaoAuthenticationProvider子类。查看这个类的关键就在这个父类中。该父类中的如下方法:publicbooleansupports(Class>authentication){return(UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));}表示我们只需要提供一个DaoAuthenticationProvider的子类来对用户进行认证。1.5自定义DaoAuthenticationProvider子类@BeanpublicDaoAuthenticationProviderdaoAuthenticationProvider(){DaoAuthenticationProviderdaoAuthen=newDaoAuthenticationProvider();daoAuthen.setPasswordEncoder(passwordEncoder());daoAuthen.setUserDetailsS??ervice(userDetailsS??ervice());daoAuthen.setHideUserNotFoundExceptions(false);returndaoAuthen;}1.6执行前面for中以下代码result=provider.authenticate(authentication);这里进入DaoAuthenticationProvider的父类AbstractUserDetailsAuthenticationProvider中的authenticate方法。该方法的核心方法retrieveUser方法在子类DaoAuthenticationProvider中实现。如果这里返回了UserDetails(查询给用户),则进入下一步Step1.7输入密码验证这里调用子类DaoAuthenticationProvider的方法,剩下就是成功后的事件处理。如果有异常,统一进行异常处理。安全登录授权认证实体类@Entity@Table(name="T_USERS")publicclassUsersimplementsUserDetails,Serializable{privatestaticfinallongserialVersionUID=1L;@Id@GeneratedValue(generator="system-uuid")@GenericGenerator(name="system-uuid",strategy="uuid")privateStringid;privateStringusername;privateStringpassword;}DAOpublicinterfaceUsersRepositoryextendsJpaRepository,JpaSpecificationExecutor{UsersfindByUsernameAndPassword(Stringusername,Stringpassword);UsersfindByUsername(Stringusername);}Security配置@ConfigurationpublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@ResourceprivateUsersRepositoryur;@ResourceprivateLogoutSuccessHandlerlogoutSuccessHandler;@BeanpublicUserDetailsS??erviceuserDetailsS??ervice(){returnusername->{Usersuser=ur.findByUsername(username);if(user==null){thrownewUsernameNotFoundException("用户名不存在");}returnuser;};}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewPasswordEncoder(){@Overridepublicbooleanmatches(CharSequencerawPassword,StringencodedPassword){returnrawPassword。等于(encodedPassword);}@OverridepublicStringencode(CharSequencerawPassword){returnrawPassword.toString();}};}@BeanpublicDaoAuthenticationProviderdaoAuthenticationProvider(){DaoAuthenticationProviderdaoAuthen=newDaoAuthenticationProvider();daoAuthen.setPasswordEncoder(passwordEncoder());daoAuthen.setUserDetailsS??ervice(userDetailsS??ervice());daoAuthen.setHideUserNotFoundExceptions(false);returndaoAuthen;}@BeanpublicSessionRegistrysessionRegistry(){returnnewSessionRegistryImpl();}//这个gi子不重新注册中的会话不失效@BeanpublicHttpSessionEventPublisherhttpSessionEventPublisher(){returnnewHttpSessionEventPublisher();}@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable().authorizeRequests().antMatchers("/pos/**").authenticated().and().formLogin().loginPage("/sign/login").and().logout().logoutSuccessHandler(logoutSuccessHandler).logoutUrl("/sign/logout");//这里配置最大同用户登录个数http.sessionManagement().maximumSessions(1).expiredUrl("/sign/login?expired").sessionRegistry(sessionRegistry());}}Controller相关接口@ControllerpublicclassLoginController{@RequestMapping("/sign/login")publicStringlogin(){return"login";}}@RestController@RequestMapping("/sign")publicclassLogoutController{@GetMapping("/logout")publicObjectlogout(HttpServletRequestrequest){HttpSessionsession=request.getSession(false);if(session!=null){session.invalidate();}SecurityContextcontext=SecurityContextHolder.getContext();context.setAuthentication(null);SecurityContextHolder.clearContext();返回“成功”;}}@RestController@RequestMapping("/pos")publicclassPosController{@GetMapping("")publicObjectget(){return"possuccess";}}//通过以下接口获取在线人数@RestController@RequestMapping("/sessions")publicclassSessionController{@ResourceprivateSessionRegistrysessionRegistry;@GetMapping("")publicObjectlist(){returnsessionRegistry.getAllPrincipals();}}测试:在chrome浏览器中用zs用户登录,用zs登录360浏览器登录后,刷新chrome浏览器登录有失败,配置的最大登录数也有效放弃!