本文已参与「新人创作礼」活动,一起开启掘金创作之路。
概述: 若依是一个后台管理系统,现在我们要在若依的基础上添加一个客户端app,那么我们要实现客户端登录(分表),把app客户端的逻辑集成进去;客户端登录是通过手机号与短信验证码实现的,而若依是通过用户名密码进行验证的; 我们的思路是这样的,若依的用户名我们可以用手机号充当,若依的密码我们用手机验证码进行充当; 改动的地方有2个地方; 分别是:
第一处改动
第二处改动 验证码形式,我们返回的密码(验证码)是从redis中进行查询的
若依的密码是从配置文件中进行读取的:
一、模仿若依的 UserDetailsServiceImpl类实现UserDetailsService接口
若依的接口实现为:
package com.ruoyi.framework.web.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
/**
* 用户验证处理
*
* @author ruoyi
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private ISysUserService userService;
@Autowired
private SysPasswordService passwordService;
@Autowired
private SysPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
System.out.println("调用后台用户认证");
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 + " 已停用");
}
passwordService.validate(user);
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user)
{
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
}
我们的接口实现为:
package com.ruoyi.framework.web.service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.ClientLoginUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.client.ClientUser;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.impl.client.ClientUserServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.Collections;
/**
* 客户端用户验证处理
*
* @author ruoyi
*/
@Service
public class ClientUserDetailsServiceImpl implements UserDetailsService
{
private static final Logger log = LoggerFactory.getLogger(ClientUserDetailsServiceImpl.class);
@Autowired
private ClientUserServiceImpl clientUserService;
@Override
public UserDetails loadUserByUsername(String phone) throws UsernameNotFoundException {
System.out.println("调用手机客户端登录逻辑");
ClientUser clientUser = clientUserService.exitsPhoneUser(phone);
if (UserStatus.DELETED.getCode().equals(clientUser.getTombstone()+""))
{
log.info("登录用户:{} 已被删除.", phone);
throw new ServiceException("对不起,您的账号:" + phone + " 已被删除");
}
else if (UserStatus.DISABLE.getCode().equals(clientUser.getState()+""))
{
log.info("登录用户:{} 已被停用.", phone);
throw new ServiceException("对不起,您的账号:" + phone + " 已停用");
}
return createClientLoginUser(clientUser);
}
public UserDetails createClientLoginUser(ClientUser clientUser)
{
return new ClientLoginUser(clientUser.getPhone());
}
}
二、模仿若依实现用户名密码验证
这里我们直接继承若依的用户名密码验证即可
package com.ruoyi.framework.config.security;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
public class ClientUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
public ClientUsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public ClientUsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}
说明:标红的地方是改动的地方
clientUserLogin方法是我们模仿若依的登录写的,login方法是若依自己写的。
/**
* 客户端登录验证
*
* @param phone 手机用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String clientUserLogin(String phone, String password, String code, String uuid)
{
//密码优化
// 用户验证
Authentication authentication = null;
try
{
ClientUsernamePasswordAuthenticationToken authenticationToken = new ClientUsernamePasswordAuthenticationToken(phone, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用ClientUserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e)
{
e.printStackTrace();
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
finally
{
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
ClientLoginUser clientLoginUser = (ClientLoginUser) authentication.getPrincipal();
//记录登录信息
recordLoginInfo(clientLoginUser.getUsername());
// 生成token
return clientTokenService.createToken(clientLoginUser);
}
/**
* 后台登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
{
boolean captchaEnabled = configService.selectCaptchaEnabled();
// 验证码开关
if (captchaEnabled)
{
validateCaptcha(username, code, uuid);
}
// 用户验证
Authentication authentication = null;
try
{
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = 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();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
三、模仿若依实现 UserDetails
3.1 若依实现
3.2 我们实现客户端的
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/*
*
* 客户端用户登录 用户及密码校验
* */
public class ClientLoginUser implements UserDetails {
@Autowired
RedisCache redisCache;
/*
* 用户手机号/客户独断登录名
* */
private String phone;
/*
* token
* */
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@Override
public String getPassword() {
ClientLoginUser clientLoginUser = (ClientLoginUser) SecurityUtils.getAuthentication().getPrincipal();
Object cacheObject = redisCache.getCacheObject(Constants.PHONE_VERIFY_PREFIX+clientLoginUser.getUsername());
String code2 = String.valueOf(cacheObject);
return code2;
}
@Override
public String getUsername() {
return this.phone;
}
public ClientLoginUser(String phone) {
this.phone = phone;
}
//------------------------------------
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}