一、登录验证时,Spring Security怎么帮我们查的用户信息
- 之前说个
SysLoginService
有登录验证的方法
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
{
boolean captchaOnOff = configService.selectCaptchaOnOff();
// 验证码开关
if (captchaOnOff)
{ //校验验证码
validateCaptcha(username, code, uuid);
}
// 用户验证
Authentication authentication = null;
try
{
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
//UsernamePasswordAuthenticationToken [Principal=com.ruoyi.common.core.domain.model.LoginUser@748cc4df, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[]]
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
//记录登录信息=====新增系统登录日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();//com.ruoyi.common.core.domain.model.LoginUser@748cc4df
//记录登录信息=====修改用户基本信息(最后IP、时间)
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
今天主要说的是其中的
authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));
这一段代码怎么(也就是spring security
怎么帮我们查的用户名、密码及用户信息)的【执行流程】===通过DEBUG
来仔细看看
二、开始
- 第一次进入
- 走到后面的其中一个方法
determineUsername
确定用户名,方法位于【抽象用户详细信息身份验证提供者org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
】
private String determineUsername(Authentication authentication) {
return (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
}
- 可以看到这个方法是三目,如果
authentication.getPrincipal() == null
【用户名不是空】,则getName()
- 上面这个方法在哪里使用呢
- 检索用户方法
retrieveUser
中 - 之后走到若依自己的用户服务
com.ruoyi.framework.web.service.UserDetailsServiceImpl
(▽)!!!到这里就没什么好说的了,懂得都懂。
- 接着user变量就有了数据
- 也可以看看上面,我们之前说的先查
username
,和没有走缓存 - 在查询结束后,还会有一些认证preAuthenticationChecks、preAuthenticationChecks、additionalAuthenticationChecks
- 最后返回【创建成功认证】
createSuccessAuthentication
createSuccessAuthentication
方法内部- 创建认证
- 代码中的官方注解
// 确保我们返回用户提供的原始凭据,
// 因此即使使用编码密码,后续尝试也能成功。
// 还要确保我们返回原始的 getDetails(),以便将来缓存到期后的
// 身份验证事件包含详细信息
三、最后说下一个常用且常见的方法getPrincipal()
位于
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
- 这就是
getPrincipal()
的数据了,就是拿user
的 - 然后,挂一张依赖图看看
四、补充上面检索用户信息【走自己的方法】
-
com.ruoyi.framework.web.service.UserDetailsServiceImpl
这个服务实现,实现自己的方法去数据查用户信息,并且给LoginUser
实体补充权限菜单
/**
* 用户验证处理
*
* @author ruoyi
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private ISysUserService userService;
@Autowired
private SysPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new ServiceException("登录用户:" + username + " 不存在");
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
}
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user)
{
/**user来自数据库
* LoginUser登录中用户需要的信息
* 这里使用了userId、deptId、整个用户信息user、用户对应权限【即用户对应的菜单】
*/
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
}
- 关键点在于
public class UserDetailsServiceImpl implements UserDetailsService
这实现了Spring Security
的接口 - 之前说的时候,经常看到的
LoginUser
也是实现了Spring Security
的UserDetails
- 在登录时,使用的实体是专门准备的
LoginUser
实体挂一份在这
public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
//...其他方法省略,感兴趣的去项目中看吧
}
那么,就到这里了。
\(^o^)/~!!!