用来干什么的?
1.用户身份认证和会话管理
2.RemeberMe和CSRF实现
3.认证流程及其核心接口详解
Spring Security的核心思想就是在filter过滤器中,处理请求。
第一步:使用Sring Security
创建spring项目,并引入spring web和Spring Security依赖
第二步:创建测试controller
@RestController@RequestMapping("/admin")public class AdminController { @GetMapping("/demo") public String demo(){ return "spring security demo"; }}
第三步:启动项目,访问 localhost:8080/admin/demo,进入Spring Security 的前端过滤器登录界面,用户名是user 密码是打印在后代的uuid,这些信息都是基于我们的内存去实现的。
源码核心类:
SecurityAutoConfiguration:自动配置类
WebSecurityConfigurerAdapter:适配器
UserDetails:处理用户登录核心类
UserDetailsService:登录核心接口类
我们可以创建一个类去实现UserDetailsService接口:
@Servicepublic class MyUserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //用户名,密码和相应的权限 UserDetails userDetails = new User("deng","123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user")); return userDetails; }}
配置成这样当我们再次访问 localhost:8080/admin/demo 时,发现登录不成功,后台报错:原因是我们密码现在是明文传输
在Spring Security4 之后,源码中的 PasswordEncoderFactories 类,对加密方式做了封装,可支持十几种加密方式。根据该类的方法提供我们只需要在密码前加上"{noop}",不适用加密方式加密,即可完成登录。如下图所示:
@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //用户名,密码和相应的权限 UserDetails userDetails = new User("deng","{noop}123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user")); return userDetails;}
我们在来测试一下官方推荐的 bcrypt 加密方式,采用hash 散列方式加密的。
@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //采用官方推荐的bctypt方式对密码进行加密 String pwd = BCrypt.hashpw("123456", BCrypt.gensalt()); //用户名,密码和相应的权限 UserDetails userDetails = new User("deng","{bcrypt}"+pwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user")); return userDetails;}
方法二:我们利用ioc容器拿到PasswordEncoder 类利用encode方法对密码加密。
@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //采用官方推荐的bctypt方式对密码进行加密 //String pwd = BCrypt.hashpw("123456", BCrypt.gensalt()); //用户名,密码和相应的权限 UserDetails userDetails = new User("deng",passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user")); return userDetails;}
在项目中创建config包
@Configuration//@EnableWebSecurity //该注解可以不要,因为该类继承WebSecurityConfigurerAdapter类。而这个类已经加了该注解public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }}
方法三:我们不让spring去管理MyUserDetailsServiceImpl,把类上的@service注解去掉,然后在config中,重写父类的userDetailsServiceBean()方法。
@Bean@Overridepublic UserDetailsService userDetailsServiceBean() throws Exception{ return new MyUserDetailsServiceImpl();}
方法四:我们可以利用WebSecurityConfigurerAdapter类,重写其configure() 方法,实现登录。此时输入其中任意一个用户信息,即可完成登录代码如下:
@Configuration//@EnableWebSecurity //该注解可以不要,因为该类继承WebSecurityConfigurerAdapter类。而这个类已经加了该注解public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //inMemoryAuthentication() 该方法时直接往内存里去放用户信息 auth.inMemoryAuthentication() .withUser("deng") .password(passwordEncoder().encode("123456")) .authorities("admin,user") .and()//这里用and方法可以指定多个用户 .withUser("fox") .password(passwordEncoder().encode("123456")) .authorities("admin"); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }// @Bean// @Override// public UserDetailsService userDetailsServiceBean() throws Exception{// return new MyUserDetailsServiceImpl();// }}