当前位置:   article > 正文

SpringSecurity框架原理浅谈之UserDetailsService

SpringSecurity框架原理浅谈之UserDetailsService

先来看一下UserDetailsService的源码和实现结构

  1. public interface UserDetailsService {
  2. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
  3. }

在该类的注释文档中,这样解释了这个方法:

加载用户特定数据的核心接口。

它作为用户DAO在整个框架中使用,并且是DaoAuthenticationProvider使用的策略。

该接口只需要一个只读方法,这简化了对新的数据访问策略的支持

对于UserDetailsService的唯一方法loadUserByUsername,文档注释也给了解释:

根据用户名定位用户。在实际实现中,搜索可能区分大小写,也可能不区分大小写,这取决于实现实例的配置方式。在这种情况下,返回的UserDetails对象的用户名可能与实际请求的用户名不同。

参数:

用户名—标识需要输入数据的用户的用户名。

返回:

一个完全填充的用户记录(从不为空)

抛出:

UsernameNotFoundException—如果找不到用户或用户没有授予权限

总的来说:

UserDetailsService的作用就是从特定的地方(通常是数据库)加载用户信息.

那UserDetailsService用到哪里呢?

前面我们提到DaoAuthenticationProvider 处理了web表单的认证逻辑,认证成功后既得到一个 Authentication(UsernamePasswordAuthenticationToken实现),里面包含了身份信息(Principal)。这个身份 信息就是一个 Object ,大多数情况下它可以被强转为UserDetails对象。

DaoAuthenticationProvider中包含了一个UserDetailsService实例,它负责根据用户名提取用户信息 UserDetails(包含密码),而后DaoAuthenticationProvider会去对比UserDetailsService提取的用户密码与用户提交的密码(存储在Authentication中)是否匹配作为认证成功的关键依据,因此可以通过将自定义的 UserDetailsService公开为spring bean来定义自定义身份验证。

下面提供一个示例,供大家参考:

  1. package com.itheima.stock.security.service;
  2. import com.google.common.base.Strings;
  3. import com.itheima.stock.mapper.SysPermissionMapper;
  4. import com.itheima.stock.mapper.SysRoleMapper;
  5. import com.itheima.stock.mapper.SysUserMapper;
  6. import com.itheima.stock.pojo.entity.SysPermission;
  7. import com.itheima.stock.pojo.entity.SysRole;
  8. import com.itheima.stock.pojo.entity.SysUser;
  9. import com.itheima.stock.pojo.vo.PermissionRespNodeVo;
  10. import com.itheima.stock.security.detail.LoginUserDetail;
  11. import com.itheima.stock.service.PermissionService;
  12. import org.apache.commons.lang3.StringUtils;
  13. import org.springframework.beans.BeanUtils;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.security.core.GrantedAuthority;
  16. import org.springframework.security.core.authority.AuthorityUtils;
  17. import org.springframework.security.core.userdetails.UserDetails;
  18. import org.springframework.security.core.userdetails.UserDetailsService;
  19. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  20. import org.springframework.stereotype.Service;
  21. import java.util.List;
  22. import java.util.Objects;
  23. import java.util.stream.Collectors;
  24. /**
  25. * @author Mr.huang
  26. * @version 1.0
  27. * @description 定义获取用户合法详情信息的服务
  28. * @date 2023/2/9 10:01
  29. */
  30. @Service
  31. public class LoginUserDetailService implements UserDetailsService {
  32. @Autowired
  33. private SysUserMapper sysUserMapper;
  34. @Autowired
  35. private SysPermissionMapper sysPermissionMapper;
  36. @Autowired
  37. private SysRoleMapper sysRoleMapper;
  38. @Autowired
  39. private PermissionService permissionService;
  40. /***
  41. * @description 当用户登录认证是,底层会自动调用LoginUserDetailService#loadUserByUsername()把登录的账户名称传入
  42. * 让开发者根据登录的用户名获取用户的信息
  43. * @param loginName
  44. * @return org.springframework.security.core.userdetails.UserDetails
  45. * @author huang
  46. * @date 2023/2/9 10:04
  47. */
  48. @Override
  49. public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
  50. //从数据库中查询用户基本信息
  51. SysUser user = sysUserMapper.findByUsername(loginName);
  52. if (Objects.isNull(user)) {
  53. throw new UsernameNotFoundException("用户不存在");
  54. }
  55. //根据用户id获取权限集合
  56. List<SysPermission> perms = sysPermissionMapper.getPermissionByUserId(user.getId());
  57. //获取权限标识集合
  58. List<String> permList = perms.stream().map(SysPermission::getPerms)
  59. .filter(StringUtils::isNoneBlank).collect(Collectors.toList());
  60. //获取当前用户对应的角色集合
  61. List<SysRole> roles = sysRoleMapper.getRolesByUserId(user.getId());
  62. //2.3 将角色集合转化成以ROLE_开头的权限表示,并与权限集合被合并
  63. List<String> roleList = roles.stream().map(r -> "ROLE_" + r.getName()).collect(Collectors.toList());
  64. //合并权限标识集合
  65. permList.addAll(roleList);
  66. //toArray(T[] a)
  67. //要求用户提供一个目标对象的泛型,在数组转换后,会返回一个指定类型的数组,不存在类型转换错误。
  68. String[] permStr = permList.toArray(new String[permList.size()]);
  69. List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(permStr);
  70. //获取树状权限菜单数据[侧边栏数据]
  71. List<PermissionRespNodeVo> tree = permissionService.getTree(perms, 0L, true);
  72. //获取用户按钮标识集合
  73. List<String> authBtnPerms = perms.stream()
  74. .filter(p -> !Strings.isNullOrEmpty(p.getCode()) && p.getType() == 3)
  75. .map(SysPermission::getCode).collect(Collectors.toList());
  76. //组装数据,这里的LoginUserDetail是UserDetail的实现类
  77. LoginUserDetail detail = new LoginUserDetail();
  78. BeanUtils.copyProperties(user,detail);
  79. detail.setAuthorities(authorityList);
  80. detail.setMenus(tree);
  81. detail.setPermissions(authBtnPerms);
  82. return detail;
  83. }
  84. }
  85.  

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号