您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~
前面运行写好的代码之所以没有任何显示,是因为还没有对Spring Security进行配置,当然啥也不显示了。这就好比你坐在车上,却不打开发动机,车子当然跑不起来。所以咱们就来让它跑起来。不过在配置之前,有必要对Spring Security的登录流程做个大致了解。
如果深入源码去了解,这个玩意及其复杂,但是没必要,知道它的机制就行了。就好比你买车也不必把发动机拆开去看它是怎么工作的吧。简单来说它就是下面这些步骤:
1、Spring Security通过AuthenticationManager接口进行身份验证
2、ProviderManager是AuthenticationManager的一个默认实现
3、ProviderManager把验证工作委托给了AuthenticationProvider接口
4、AuthenticationProvider的实现类DaoAuthenticationProvider会检查身份认证
5、DaoAuthenticationProvider又把认证工作委托给了UserDetailsService接口
6、自定义UserDetailsService类从数据库中获取用户账号、密码、角色等信息,然后封装成UserDetails返回
7、使用Spring Security还需要自定义AuthenticationProvider接口,获取用户输入的账号、密码等信息,并封装成Authentication接口
8、将UserDetails和Authentication进行比对,如果一致就返回UsernamePasswordAuthenticationToken,否则抛出异常
下面是认证流程图:
首先重写loadUserByUsername:
/*** 自定义用户详情** @author 湘王*/
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserService userService;@Autowiredprivate RoleService roleService;@Autowiredprivate UserRoleService userRoleService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Collection authorities = new ArrayList<>();// 从数据库中取出用户信息SysUser user = userService.getByName(username);// 判断用户是否存在if(null == user) {System.out.println("user is not exist");throw new UsernameNotFoundException("user is not exist");}// 获得用户角色:方式一List list = userRoleService.getByUserId(user.getId());
// // 获得用户角色:方式二
// List list = roleService.getByUserId(user.getId());// // 给用户添加授权:方式一
// for (SysUserRole userRole : list) {
// SysRole role = roleService.getById(userRole.getRoleid());
// authorities.add(new SimpleGrantedAuthority(role.getName()));
// }
// // 返回UserDetails实现类
// return new User(user.getName(), user.getPassword(), authorities);// 给用户添加授权:方式二return User.withUsername(username).password(user.getPassword()).authorities(list.stream().filter(Objects::nonNull)// 判断是否为空.map(userRole -> roleService.getById(userRole.getRoleid()))// 从SysUserRole获取Role.map(SysRole::getName)// 转变为角色名称字符串.map(SimpleGrantedAuthority::new)// 依据角色名称创建SimpleGrantedAuthority.toArray(SimpleGrantedAuthority[]::new)// list转变为数组).build();}
}
因为UserDetailsService返回了封装的UserDetails,所以需要再自定义AuthenticationProvider返回Authentication接口:
/*** 自定义登录验证** @author 湘王*/
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {// 获取表单输入中返回的用户名String username = (String) authentication.getPrincipal();// 获取表单中输入的密码String password = (String) authentication.getCredentials();// 这里调用我们的自己写的获取用户的方法UserDetails userInfo = customUserDetailsService.loadUserByUsername(username);if (userInfo == null) {System.out.println("user is not exist");throw new UsernameNotFoundException("user is not exist");}PasswordEncoder passwordEncoder = new PasswordEncoder() {@Overridepublic String encode(CharSequence charSequence) {return charSequence.toString();}@Overridepublic boolean matches(CharSequence charSequence, String s) {return s.equals(charSequence.toString());}};// 采用简单密码验证if (!passwordEncoder.matches(password, userInfo.getPassword())) {System.out.println("user or password error");throw new BadCredentialsException("user or password error");}Collection extends GrantedAuthority> authorities = userInfo.getAuthorities();// 构建返回的用户登录成功的tokenreturn new UsernamePasswordAuthenticationToken(userInfo, password, authorities);}@Overridepublic boolean supports(Class> aClass) {return true;}
}
接着来实现实现WebSecurityConfigurerAdapter,它通过重写WebSecurityConfigurerAdapter中的相关方法(一般是configurer)来自定义配置。WebSecurityConfigurerAdapter主要做几件事:
1、初始化
2、开启Security
3、配置各种过滤器,实现验证过滤器链
下面是它的代码:
/*** spring security验证配置** @author 湘王*/
// 配置类
@Configuration
// 开启Security服务
@EnableWebSecurity
// 开启全局Securtiy注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Autowiredprivate CustomAuthenticationProvider authenticationProvider;// 自定义的登录验证逻辑@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(authenticationProvider);}// 控制逻辑@Overrideprotected void configure(HttpSecurity http) throws Exception {// 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);http.authorizeRequests().anyRequest().authenticated()// 设置自定义认证成功、失败及登出处理器.and().formLogin().loginPage("/login").and().cors().and().csrf().disable();}@Overridepublic void configure(WebSecurity web) throws Exception {// 设置拦截忽略文件夹,可以对静态资源放行web.ignoring().antMatchers("/css/**", "/js/**");}
}
接着用postman进行测试:
回顾整个调用过程,它的时序图是:
但是等等:好像除了/login,其他方法都不能正常访问!
感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~