• Spring Security(3)


    您好,我是湘王,这是我的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:

    1. /**
    2. * 自定义用户详情
    3. *
    4. * @author 湘王
    5. */
    6. @Service("userDetailsService")
    7. public class CustomUserDetailsService implements UserDetailsService {
    8. @Autowired
    9. private UserService userService;
    10. @Autowired
    11. private RoleService roleService;
    12. @Autowired
    13. private UserRoleService userRoleService;
    14. @Override
    15. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    16. Collection authorities = new ArrayList<>();
    17. // 从数据库中取出用户信息
    18. SysUser user = userService.getByName(username);
    19. // 判断用户是否存在
    20. if(null == user) {
    21. System.out.println("user is not exist");
    22. throw new UsernameNotFoundException("user is not exist");
    23. }
    24. // 获得用户角色:方式一
    25. List list = userRoleService.getByUserId(user.getId());
    26. // // 获得用户角色:方式二
    27. // List list = roleService.getByUserId(user.getId());
    28. // // 给用户添加授权:方式一
    29. // for (SysUserRole userRole : list) {
    30. // SysRole role = roleService.getById(userRole.getRoleid());
    31. // authorities.add(new SimpleGrantedAuthority(role.getName()));
    32. // }
    33. // // 返回UserDetails实现类
    34. // return new User(user.getName(), user.getPassword(), authorities);
    35. // 给用户添加授权:方式二
    36. return User
    37. .withUsername(username)
    38. .password(user.getPassword())
    39. .authorities(list.stream()
    40. .filter(Objects::nonNull)// 判断是否为空
    41. .map(userRole -> roleService.getById(userRole.getRoleid()))// 从SysUserRole获取Role
    42. .map(SysRole::getName)// 转变为角色名称字符串
    43. .map(SimpleGrantedAuthority::new)// 依据角色名称创建SimpleGrantedAuthority
    44. .toArray(SimpleGrantedAuthority[]::new)// list转变为数组
    45. ).build();
    46. }
    47. }

    因为UserDetailsService返回了封装的UserDetails,所以需要再自定义AuthenticationProvider返回Authentication接口:

    1. /**
    2. * 自定义登录验证
    3. *
    4. * @author 湘王
    5. */
    6. @Component
    7. public class CustomAuthenticationProvider implements AuthenticationProvider {
    8. @Autowired
    9. private CustomUserDetailsService customUserDetailsService;
    10. @Override
    11. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    12. // 获取表单输入中返回的用户名
    13. String username = (String) authentication.getPrincipal();
    14. // 获取表单中输入的密码
    15. String password = (String) authentication.getCredentials();
    16. // 这里调用我们的自己写的获取用户的方法
    17. UserDetails userInfo = customUserDetailsService.loadUserByUsername(username);
    18. if (userInfo == null) {
    19. System.out.println("user is not exist");
    20. throw new UsernameNotFoundException("user is not exist");
    21. }
    22. PasswordEncoder passwordEncoder = new PasswordEncoder() {
    23. @Override
    24. public String encode(CharSequence charSequence) {
    25. return charSequence.toString();
    26. }
    27. @Override
    28. public boolean matches(CharSequence charSequence, String s) {
    29. return s.equals(charSequence.toString());
    30. }
    31. };
    32. // 采用简单密码验证
    33. if (!passwordEncoder.matches(password, userInfo.getPassword())) {
    34. System.out.println("user or password error");
    35. throw new BadCredentialsException("user or password error");
    36. }
    37. Collectionextends GrantedAuthority> authorities = userInfo.getAuthorities();
    38. // 构建返回的用户登录成功的token
    39. return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    40. }
    41. @Override
    42. public boolean supports(Class aClass) {
    43. return true;
    44. }
    45. }

    接着来实现实现WebSecurityConfigurerAdapter,它通过重写WebSecurityConfigurerAdapter中的相关方法(一般是configurer)来自定义配置。WebSecurityConfigurerAdapter主要做几件事:

    1、初始化

    2、开启Security

    3、配置各种过滤器,实现验证过滤器链

    下面是它的代码:

    1. /**
    2. * spring security验证配置
    3. *
    4. * @author 湘王
    5. */
    6. // 配置类
    7. @Configuration
    8. // 开启Security服务
    9. @EnableWebSecurity
    10. // 开启全局Securtiy注解
    11. @EnableGlobalMethodSecurity(prePostEnabled = true)
    12. public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    13. @Autowired
    14. private CustomUserDetailsService customUserDetailsService;
    15. @Autowired
    16. private CustomAuthenticationProvider authenticationProvider;
    17. // 自定义的登录验证逻辑
    18. @Override
    19. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    20. auth.authenticationProvider(authenticationProvider);
    21. }
    22. // 控制逻辑
    23. @Override
    24. protected void configure(HttpSecurity http) throws Exception {
    25. // 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤
    26. http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);
    27. http.authorizeRequests()
    28. .anyRequest().authenticated()
    29. // 设置自定义认证成功、失败及登出处理器
    30. .and().formLogin().loginPage("/login")
    31. .and().cors()
    32. .and().csrf().disable();
    33. }
    34. @Override
    35. public void configure(WebSecurity web) throws Exception {
    36. // 设置拦截忽略文件夹,可以对静态资源放行
    37. web.ignoring().antMatchers("/css/**", "/js/**");
    38. }
    39. }

    接着用postman进行测试:

     

    回顾整个调用过程,它的时序图是:

     

    但是等等:好像除了/login,其他方法都不能正常访问!


    感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

  • 相关阅读:
    狂神说之redis笔记
    AI芯片技术-2022年
    【Python】基本数据类型(二)数字类型的运算符
    SWAT-MODFLOW地表水与地下水耦合
    聊聊身边的嵌入式:点菜机用着好好的,突然挂了,这口锅应该甩给谁?
    OpenSIPS 防扫描处理
    涂鸦云平台设备授权介绍
    react的1.函数组件2.usestate3.useeffect4.ref5.fragment6.contex 代码加注释
    科技云报道:Citrix正式退出中国市场!国产们谁能真正顶上?
    amlogic 多wifi 多bluetooh 兼容方案
  • 原文地址:https://blog.csdn.net/lostrex/article/details/127991219