SpringSecurity认证初探

本文探讨了SpringSecurity的认证流程,包括默认的认证方式,即通过InMemoryUserDetailsManager进行用户验证。关键修改在于封装请求参数为UsernamePasswordAuthenticationToken,并实现UserDetailsService接口的loadUserByUsername()方法。解决方案分为两步:一是封装参数并调用authenticate()方法;二是创建UserDetailServiceImpl实现类以自定义用户验证逻辑。

SpringSecurity认证流程图

默认方式:

  • SpringSecurity需要先将请求传过来的userName和password进行封装以后才能执行到后续的认证。
  • SpringSecurity内置的默认登录页是通过 InMemoryUserDetailsManager 实现类中的loadUserByUsername()方法去做的校验。

关键修改:

结合上述默认认证方式的校验,所以要修改的关键部分如下:

  • 封装传入参数为UsernamePasswordAuthenticationToken类型
  • 调用AuthenticationManager类中的的 authenticate() 方法传入参数为封装好的UsernamePasswordAuthenticationToken类型对象authenticationToken
  • 主要修改的部分就是实现 UserDetailsService 接口的 loadUserByUsername() 方法
  • 重点在6.1部分

解决方案:

第一步:

封装请求参数然后调用authenticate()方法

    /**
     * 登录
     *
     * @param sysUser
     * @return
     */
    public AjaxResult login(SysUser sysUser) {
        // 用户验证
        Authentication authentication = null;
        // 这里捕获认证时密码错误的异常
        try {
            //AuthenticationManager authenticate进行用户认证
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                    sysUser.getUserName(), sysUser.getPassword());
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                //DaoAuthenticationProvider类下的方法additionalAuthenticationChecks()方法中matches抛出的密码错误异常
                throw new UserException(HttpStatus.UNAUTHORIZED, "密码错误");
            } else {
                throw new UserException(HttpStatus.ERROR, e.getMessage());
            }
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 不允许账户多端登录情况时,先删除之前用户登录信息的缓存
        if (!soloLogin) {
            String userIdKey=CacheConstants.LOGIN_USER_ID_KEY.concat(String.valueOf(loginUser.getSysUser().getUserId()));
            String userKey= redisService.getCacheObject(userIdKey);
            if (StringUtils.isNotEmpty(userKey)){
                redisService.deleteObject(userIdKey);// 删除用户登录编号缓存
                redisService.deleteObject(userKey);// 删除用户信息缓存
            }
        }
        // 生成token
        String token = tokenService.createToken(loginUser);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("access_token", TokenConstants.PREFIX.concat(token));

        return AjaxResult.success("登录成功", jsonObject);

    }

第二步:

写一个UserDetailServiceImpl实现类,去实现 UserDetailsService 接口的 loadUserByUsername() 方法

/**
 * 实现 UserDetailsService接口 重写其方法loadUserByUsername
 *
 * @Author: loong
 * @CreateTime: 2022-12-12
 */
@Service
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MenuMapper menuMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户信息
        SysUser user = new SysUser();
        user.setUserName(username);
        SysUser sysUser = userMapper.selectUserInfoByUser(user);
        // 如果没查到用户就抛出异常
        if (Objects.isNull(sysUser)) {
            log.info("登录用户:{}不存在", username);
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "用户不存在");
        }
        // 查询对应权限
        List<String> permissionsList = menuMapper.selectPermissionsByUserId(sysUser.getUserId());
        Set<String> permissionsSet = new HashSet<>();
        for (String p : permissionsList) {
            permissionsSet.addAll(Arrays.asList(p.split(",")));
        }
        // 将数据封装成UserDetails返回
        return new LoginUser(sysUser, permissionsSet);
    }
}

**

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值