SpringSecurity框架原理浅谈之UserDetailsService
UserDetailsService的作用就是从特定的地方(通常是数据库)加载用户信息.
先来看一下UserDetailsService的源码和实现结构
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

在该类的注释文档中,这样解释了这个方法:
加载用户特定数据的核心接口。
它作为用户DAO在整个框架中使用,并且是DaoAuthenticationProvider使用的策略。
该接口只需要一个只读方法,这简化了对新的数据访问策略的支持
对于UserDetailsService的唯一方法loadUserByUsername,文档注释也给了解释:
根据用户名定位用户。在实际实现中,搜索可能区分大小写,也可能不区分大小写,这取决于实现实例的配置方式。在这种情况下,返回的UserDetails对象的用户名可能与实际请求的用户名不同。
参数:
用户名—标识需要输入数据的用户的用户名。
返回:
一个完全填充的用户记录(从不为空)
抛出:
UsernameNotFoundException—如果找不到用户或用户没有授予权限
总的来说:
UserDetailsService的作用就是从特定的地方(通常是数据库)加载用户信息.
那UserDetailsService用到哪里呢?
前面我们提到DaoAuthenticationProvider 处理了web表单的认证逻辑,认证成功后既得到一个 Authentication(UsernamePasswordAuthenticationToken实现),里面包含了身份信息(Principal)。这个身份 信息就是一个 Object ,大多数情况下它可以被强转为UserDetails对象。
DaoAuthenticationProvider中包含了一个UserDetailsService实例,它负责根据用户名提取用户信息 UserDetails(包含密码),而后DaoAuthenticationProvider会去对比UserDetailsService提取的用户密码与用户提交的密码(存储在Authentication中)是否匹配作为认证成功的关键依据,因此可以通过将自定义的 UserDetailsService公开为spring bean来定义自定义身份验证。
下面提供一个示例,供大家参考:
package com.itheima.stock.security.service;
import com.google.common.base.Strings;
import com.itheima.stock.mapper.SysPermissionMapper;
import com.itheima.stock.mapper.SysRoleMapper;
import com.itheima.stock.mapper.SysUserMapper;
import com.itheima.stock.pojo.entity.SysPermission;
import com.itheima.stock.pojo.entity.SysRole;
import com.itheima.stock.pojo.entity.SysUser;
import com.itheima.stock.pojo.vo.PermissionRespNodeVo;
import com.itheima.stock.security.detail.LoginUserDetail;
import com.itheima.stock.service.PermissionService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author Mr.huang
* @version 1.0
* @description 定义获取用户合法详情信息的服务
* @date 2023/2/9 10:01
*/
@Service
public class LoginUserDetailService implements UserDetailsService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private SysPermissionMapper sysPermissionMapper;
@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private PermissionService permissionService;
/***
* @description 当用户登录认证是,底层会自动调用LoginUserDetailService#loadUserByUsername()把登录的账户名称传入
* 让开发者根据登录的用户名获取用户的信息
* @param loginName
* @return org.springframework.security.core.userdetails.UserDetails
* @author huang
* @date 2023/2/9 10:04
*/
@Override
public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
//从数据库中查询用户基本信息
SysUser user = sysUserMapper.findByUsername(loginName);
if (Objects.isNull(user)) {
throw new UsernameNotFoundException("用户不存在");
}
//根据用户id获取权限集合
List<SysPermission> perms = sysPermissionMapper.getPermissionByUserId(user.getId());
//获取权限标识集合
List<String> permList = perms.stream().map(SysPermission::getPerms)
.filter(StringUtils::isNoneBlank).collect(Collectors.toList());
//获取当前用户对应的角色集合
List<SysRole> roles = sysRoleMapper.getRolesByUserId(user.getId());
//2.3 将角色集合转化成以ROLE_开头的权限表示,并与权限集合被合并
List<String> roleList = roles.stream().map(r -> "ROLE_" + r.getName()).collect(Collectors.toList());
//合并权限标识集合
permList.addAll(roleList);
//toArray(T[] a)
//要求用户提供一个目标对象的泛型,在数组转换后,会返回一个指定类型的数组,不存在类型转换错误。
String[] permStr = permList.toArray(new String[permList.size()]);
List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(permStr);
//获取树状权限菜单数据[侧边栏数据]
List<PermissionRespNodeVo> tree = permissionService.getTree(perms, 0L, true);
//获取用户按钮标识集合
List<String> authBtnPerms = perms.stream()
.filter(p -> !Strings.isNullOrEmpty(p.getCode()) && p.getType() == 3)
.map(SysPermission::getCode).collect(Collectors.toList());
//组装数据,这里的LoginUserDetail是UserDetail的实现类
LoginUserDetail detail = new LoginUserDetail();
BeanUtils.copyProperties(user,detail);
detail.setAuthorities(authorityList);
detail.setMenus(tree);
detail.setPermissions(authBtnPerms);
return detail;
}
}
更多推荐
所有评论(0)