定义一个登录接口
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
然后调用service
public String login(String username, String password, String code, String uuid) {
// 验证码校验
this.validateCaptcha(username, code, uuid);
// 登录前置校验
this.loginPreCheck(username, password);
// 用户验证
Authentication authentication = null;
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = this.authenticationManager.authenticate(authenticationToken);
} 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());
}
} finally {
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
this.recordLoginInfo(loginUser.getUserId());
// 生成token
return this.tokenService.createToken(loginUser);
}
1、将前端传入的用户名和密码传入 这个类 UsernamePasswordAuthenticationToken,生成 UsernamePasswordAuthenticationToken 对象;
2、在service层注入这个 AuthenticationManager 接口,通过这个接口 去调用 authenticate 这个方法,然后将UsernamePasswordAuthenticationToken对象传入,返回 Authentication 对象
3、在调用这个 authenticate 方法的时候,内部会调用 UserDetailsService 这个接口的 loadUserByUsername 这个方法,我们需要创建一个实现类去 是现在这个 UserDetailsService 接口,在实现类里面的loadUserByUsername这个方法 自定义查询数据库的用户名和密码等逻辑;然后返回 一个 LoginUser 对象;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = this.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 + " 已停用");
}
this.passwordService.validate(user);
LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, this.permissionService.getMenuPermission(user));
return loginUser;
}
LoginUser对象 去接受 返回数据
package com.msm.common.core.domain.model;
import com.alibaba.fastjson2.annotation.JSONField;
import com.msm.common.core.domain.entity.SysUser;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
/**
* 登录用户身份权限
*
* @author msm
*/
@Data
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 permissions;
/**
* 用户信息
*/
private SysUser user;
public LoginUser() {
}
public LoginUser(SysUser user, Set permissions) {
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword() {
return this.user.getPassword();
}
@Override
public String getUsername() {
return this.user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
}
然后创建令牌,这里采用jwt(json web token)生成token
/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
this.setUserAgent(loginUser);
this.refreshToken(loginUser);
Map claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return this.createToken(claims);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map claims) {
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, this.secret).compact();
return token;
}