说明:JwtUserDetails自定义实现了UserDetails类,增加username和password字段,除此之外,还可以扩展存储更多用户信息,例如,身份证,手机号,邮箱等等。其作用在于可构建成一个用户安全模型,用于装载从数据库查询出来的用户及权限信息。
2.通过钥匙username获取到宝箱方法:
1 /** 2 * 用户登录认证信息查询 3 * 4 * @author zhujiqian 5 * @date 2020/7/30 15:30 6 */ 7 @Service 8 public class UserDetailsServiceImpl implements UserDetailsService { 9 10 @Resource 11 private SysUserService sysUserService; 12 13 @Override 14 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 15 SysUser user = sysUserService.findByName(username); 16 if (user == null) { 17 throw new UsernameNotFoundException("该用户不存在"); 18 } 19 20 Set<String> permissions = sysUserService.findPermissions(user.getName()); 21 List<GrantedAuthority> grantedAuthorities = permissions.stream().map(AuthorityImpl::new).collect(Collectors.toList()); 22 return new JwtUserDetails(user.getName(), user.getPassword(), user.getSalt(), grantedAuthorities); 23 } 24 }这个自定义的UserDetailsServiceImpl类实现了Spring Security框架自带的UserDetailsService接口,这个接口只定义一个简单的loadUserByUsername方法:
1 public interface UserDetailsService { 2 UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 3 }根据loadUserByUsername方法名便能看出,这是一个可根据username用户名获取到User对象信息的方法,并返回一个UserDetails对象,即前头的“宝箱里的通行信息”,换言之,通过重写这个方法,我们能在该方法里实现用户登录认证信息的查询,并返回对应查询信息。
综合以上代码,先用开头提到的宝箱意象做一个总结,即拿着userName这把钥匙,通过loadUserByUsername这个方法指引,可进入到山洞(数据库),去寻找能打开的宝箱(在数据库里select查询userName对应数据),若能打开其中一个宝箱(即数据库里存在userName对应的数据),则获取宝箱里的通行信息(实现UserDetails的JwtUserDetails对象信息)。
3.关口通行过往检查设置
自定义的SecurityConfig配置类是SpringBoot整合Spring Security的关键灵魂所在。该配置信息会在springboot启动时进行加载。其中,authenticationManager() 会创建一个可用于传token做认证的AuthenticationManager对象,而AuthenticationManagerBuilder中的auth.authenticationProvider()则会创建一个provider提供者,并将userDetailsService注入进去,该userDetailsService的子类被自定义的UserDetailsServiceImpl类继承,并重写loadUserByUsername()方法,因此,当源码里执行userDetailsService的loadUserByUsername()方法时,即会执行被重写的子类loadUserByUsername()方法。
由此可见,在做认证的过程中,只需找到注入userDetailsService的provider对象,即可执行loadUserByUsername去根据username获取数据库里信息。
那具体是在哪个provider对象?请看下面详细解析。
1 @Configuration 2 @EnableWebSecurity 3 @EnableGlobalMethodSecurity(prePostEnabled = true) 4 public class SecurityConfig extends WebSecurityConfigurerAdapter { 5 6 @Resource 7 private UserDetailsService userDetailsService; 8 9 @Override 10 public void configure(AuthenticationManagerBuilder auth) { 11 auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService)); 12 } 13 14 @Bean 15 @Override 16 public AuthenticationManager authenticationManager() throws Exception { 17 return super.authenticationManager(); 18 } 19 20 @Override 21 protected void configure(HttpSecurity httpSecurity) throws Exception { 22 //使用的是JWT,禁用csrf 23 httpSecurity.cors().and().csrf().disable() 24 //设置请求必须进行权限认证 25 .authorizeRequests() 26 //跨域预检请求 27 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 28 //permitAll()表示所有用户可认证 29 .antMatchers( "/webjars/**").permitAll() 30 //首页和登录页面 31 .antMatchers("http://www.likecs.com/").permitAll() 32 .antMatchers("/login").permitAll() 33 // 验证码 34 .antMatchers("/captcha.jpg**").permitAll() 35 // 其他所有请求需要身份认证 36 .anyRequest().authenticated(); 37 //退出登录处理 38 httpSecurity.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()); 39 //token验证过滤器 40 httpSecurity.addFilterBefore(new JwtAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class); 41 } 42 }首先,双击SecurityConfig 类里的JwtAuthenticationProvider——