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

被折叠的 条评论
为什么被折叠?



