详解spring security 配置多个AuthenticationProvider
前言
发现很少关于springsecurity的文章,基本都是入门级的,配个UserServiceDetails或者配个路由控制就完事了,而且很多还是xml配置,国内通病...so,本文里的配置都是java配置,不涉及xml配置,事实上我也不会xml配置
springsecurity的大体介绍
springsecurity本身如果只是说配置,还是很简单易懂的(我也不知道网上说springsecurity难,难在哪里),简单不需要特别的功能,一个WebSecurityConfigurerAdapter的实现,然后实现UserServiceDetails就是简单的数据库验证了,这个我就不说了。
springsecurity大体上是由一堆Filter(所以才能在springmvc前拦截请求)实现的,Filter有几个,登出Filter(LogoutFilter),用户名密码验证Filter(UsernamePasswordAuthenticationFilter)之类的,Filter再交由其他组件完成细分的功能,例如最常用的UsernamePasswordAuthenticationFilter会持有一个AuthenticationManager引用,AuthenticationManager顾名思义,验证管理器,负责验证的,但AuthenticationManager本身并不做具体的验证工作,AuthenticationManager持有一个AuthenticationProvider集合,AuthenticationProvider才是做验证工作的组件,AuthenticationManager和AuthenticationProvider的工作机制可以大概看一下这两个的javadoc,然后成功失败都有相对应该Handler。大体的springsecurity的验证工作流程就是这样了。
开始配置多AuthenticationProvider
首先,写一个内存认证的AuthenticationProvider,这里我简单地写一个只有root帐号的AuthenticationProvider
packagecom.scau.equipment.config.common.security.provider; importorg.springframework.security.authentication.AuthenticationProvider; importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken; importorg.springframework.security.core.Authentication; importorg.springframework.security.core.AuthenticationException; importorg.springframework.security.core.GrantedAuthority; importorg.springframework.security.core.authority.SimpleGrantedAuthority; importorg.springframework.security.core.userdetails.User; importorg.springframework.stereotype.Component; importjava.util.Arrays; importjava.util.List; /** *CreatedbyAdministratoron2017-05-10. */ @Component publicclassInMemoryAuthenticationProviderimplementsAuthenticationProvider{ privatefinalStringadminName="root"; privatefinalStringadminPassword="root"; //根用户拥有全部的权限 privatefinalListauthorities=Arrays.asList(newSimpleGrantedAuthority("CAN_SEARCH"), newSimpleGrantedAuthority("CAN_SEARCH"), newSimpleGrantedAuthority("CAN_EXPORT"), newSimpleGrantedAuthority("CAN_IMPORT"), newSimpleGrantedAuthority("CAN_BORROW"), newSimpleGrantedAuthority("CAN_RETURN"), newSimpleGrantedAuthority("CAN_REPAIR"), newSimpleGrantedAuthority("CAN_DISCARD"), newSimpleGrantedAuthority("CAN_EMPOWERMENT"), newSimpleGrantedAuthority("CAN_BREED")); @Override publicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{ if(isMatch(authentication)){ Useruser=newUser(authentication.getName(),authentication.getCredentials().toString(),authorities); returnnewUsernamePasswordAuthenticationToken(user,authentication.getCredentials(),authorities); } returnnull; } @Override publicbooleansupports(Class>authentication){ returntrue; } privatebooleanisMatch(Authenticationauthentication){ if(authentication.getName().equals(adminName)&&authentication.getCredentials().equals(adminPassword)) returntrue; else returnfalse; } }
support方法检查authentication的类型是不是这个AuthenticationProvider支持的,这里我简单地返回true,就是所有都支持,这里所说的authentication为什么会有多个类型,是因为多个AuthenticationProvider可以返回不同的Authentication。
publicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException方法就是验证过程。
如果AuthenticationProvider返回了null,AuthenticationManager会交给下一个支持authentication类型的AuthenticationProvider处理。
另外需要一个数据库认证的AuthenticationProvider,我们可以直接用springsecurity提供的DaoAuthenticationProvider,设置一下UserServiceDetails和PasswordEncoder就可以了
@Bean DaoAuthenticationProviderdaoAuthenticationProvider(){ DaoAuthenticationProviderdaoAuthenticationProvider=newDaoAuthenticationProvider(); daoAuthenticationProvider.setPasswordEncoder(newBCryptPasswordEncoder()); daoAuthenticationProvider.setUserDetailsService(userServiceDetails); returndaoAuthenticationProvider; }
最后在WebSecurityConfigurerAdapter里配置一个含有以上两个AuthenticationProvider的AuthenticationManager,依然重用springsecurity提供的ProviderManager
packagecom.scau.equipment.config.common.security; importcom.scau.equipment.config.common.security.handler.AjaxLoginFailureHandler; importcom.scau.equipment.config.common.security.handler.AjaxLoginSuccessHandler; importcom.scau.equipment.config.common.security.provider.InMemoryAuthenticationProvider; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.security.authentication.AuthenticationManager; importorg.springframework.security.authentication.ProviderManager; importorg.springframework.security.authentication.dao.DaoAuthenticationProvider; importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; importorg.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; importorg.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer; importorg.springframework.security.config.annotation.web.builders.HttpSecurity; importorg.springframework.security.config.annotation.web.builders.WebSecurity; importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; importorg.springframework.security.core.GrantedAuthority; importorg.springframework.security.core.authority.SimpleGrantedAuthority; importorg.springframework.security.core.userdetails.UserDetailsService; importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; importjava.util.Arrays; importjava.util.List; /** *CreatedbyAdministratoron2017/2/17. */ @Configuration publicclassSecurityConfigextendsWebSecurityConfigurerAdapter{ @Autowired UserDetailsServiceuserServiceDetails; @Autowired InMemoryAuthenticationProviderinMemoryAuthenticationProvider; @Bean DaoAuthenticationProviderdaoAuthenticationProvider(){ DaoAuthenticationProviderdaoAuthenticationProvider=newDaoAuthenticationProvider(); daoAuthenticationProvider.setPasswordEncoder(newBCryptPasswordEncoder()); daoAuthenticationProvider.setUserDetailsService(userServiceDetails); returndaoAuthenticationProvider; } @Override protectedvoidconfigure(HttpSecurityhttp)throwsException{ http .csrf().disable() .rememberMe().alwaysRemember(true).tokenValiditySeconds(86400).and() .authorizeRequests() .antMatchers("/","/*swagger*/**","/v2/api-docs").permitAll() .anyRequest().authenticated().and() .formLogin() .loginPage("/") .loginProcessingUrl("/login") .successHandler(newAjaxLoginSuccessHandler()) .failureHandler(newAjaxLoginFailureHandler()).and() .logout().logoutUrl("/logout").logoutSuccessUrl("/"); } @Override publicvoidconfigure(WebSecurityweb)throwsException{ web.ignoring().antMatchers("/public/**","/webjars/**","/v2/**","/swagger**"); } @Override protectedAuthenticationManagerauthenticationManager()throwsException{ ProviderManagerauthenticationManager=newProviderManager(Arrays.asList(inMemoryAuthenticationProvider,daoAuthenticationProvider())); //不擦除认证密码,擦除会导致TokenBasedRememberMeServices因为找不到Credentials再调用UserDetailsService而抛出UsernameNotFoundException authenticationManager.setEraseCredentialsAfterAuthentication(false); returnauthenticationManager; } /** *这里需要提供UserDetailsService的原因是RememberMeServices需要用到 *@return */ @Override protectedUserDetailsServiceuserDetailsService(){ returnuserServiceDetails; } }
基本上都是重用了原有的类,很多都是默认使用的,只不过为了修改下行为而重新配置。其实如果偷懒,直接用一个UserDetailsService,在里面做各种认证也是可以的~不过这样就没意思了
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。