SpringSecurity框架原理浅谈之UserDetailsService

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

public interface UserDetailsService {
    
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

}

SpringSecurity框架原理浅谈之UserDetailsService_第1张图片

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

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

它作为用户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 perms = sysPermissionMapper.getPermissionByUserId(user.getId());
        //获取权限标识集合
        List permList = perms.stream().map(SysPermission::getPerms)
                .filter(StringUtils::isNoneBlank).collect(Collectors.toList());

        //获取当前用户对应的角色集合
        List roles = sysRoleMapper.getRolesByUserId(user.getId());
        //2.3 将角色集合转化成以ROLE_开头的权限表示,并与权限集合被合并
        List 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 authorityList = AuthorityUtils.createAuthorityList(permStr);
        //获取树状权限菜单数据[侧边栏数据]
        List tree = permissionService.getTree(perms, 0L, true);
        //获取用户按钮标识集合
        List 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;
    }
}
 

你可能感兴趣的:(java,spring,前端)