上一章我们讲了用户管理模块开发,到此我们的rbac模块 资源管理、角色管理、用户管理都已开发完。这一章我们开发用户登录模块,基于用户名和密码登录,鉴权我们用的是token,使用的是redis来管理token。
1.创建工程目录
用户登录功能在server工程下开发,创建controller、filter、pojo、service、startup工程目录
2. 获取token
登录我分了两步去做的,首先去获取初始化一个随机token,token用于给用户鉴权。初始化的token状态是false,只有登录成功以后token状态会变成true。缓存在redis中过期时间默认给的是30分钟。
创建一个UserLoginController
package com.swh.server.controller;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.common.result.AjaxResult;
import com.swh.common.utils.JackJsonUtil;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
import com.swh.server.service.UserLoginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import static com.swh.common.constant.CommonConst.NotInterceptUrl.ADMIN_USER_LOGIN;
import static com.swh.common.constant.CommonConst.NotInterceptUrl.GENERATE_KEY;
import static com.swh.common.constant.CommonConst.SWH_TOKEN;
/**
* @ClassName: UserLoginController
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:33
*/
@Slf4j
@Api(tags = "登录")
@RestController
public class UserLoginController {
@Autowired
private UserLoginService userLoginService;
@ApiOperation(value = "获取token,用-分割,最后一节是用于加密的AES密钥")
@GetMapping(GENERATE_KEY)
public AjaxResult<String> getAesKey(@RequestHeader(value = SWH_TOKEN, required = false) String token) {
return AjaxResult.ok(userLoginService.getAesKey(token));
}
}
创建一个UserLoginService
package com.swh.server.service;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
/**
* @ClassName: UserLoginService
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:35
*/
public interface UserLoginService {
/**
* 获取token
*
* @param token
* @return
*/
String getAesKey(String token);
}
创建一个UserLoginServiceImpl,实现生成token的具体逻辑
package com.swh.server.service.impl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import com.swh.admin.dao.AdminResDao;
import com.swh.admin.dao.AdminUserDao;
import com.swh.admin.dao.RoleResMpDao;
import com.swh.admin.dao.UserRoleMpDao;
import com.swh.admin.pojo.db.DbAdminResVo;
import com.swh.admin.pojo.db.DbAdminUserVo;
import com.swh.admin.pojo.resp.RespAdminResTreeVo;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.admin.service.AdminResService;
import com.swh.common.exception.RequestException;
import com.swh.common.utils.*;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
import com.swh.server.service.UserLoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.swh.common.constant.AdminUserConst.MenuLevle.LEVLE_THREE;
import static com.swh.common.constant.CommonConst.*;
import static com.swh.common.constant.CommonConst.CacheKey.REDIS_ADMIN_USER_LOGIN;
import static com.swh.common.result.ResultCode.USERNAME_OR_PASSWORD_INVALID;
/**
* @ClassName: UserLoginServiceImpl
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:35
*/
@Slf4j
@Service
public class UserLoginServiceImpl implements UserLoginService {
@Override
public String getAesKey(String t) {
if (StrUtil.isBlank(t)) {
String key = RandomUtil.randomString(16);
LOGGER.debug("首次访问获取的AES key:{}", key);
String token = "swh-" + DbIdUtil.getInstance().nextUuid() + "-" + key;
redisCacheUtil.hashPut(token, LOGIN_STATUS, "false", SESSION_TIME, TimeUnit.MINUTES);
return token;
} else {
return t;
}
}
}
2. 本地登录
登录逻辑主要分为 ①:校验用户是否存在;②:验证用户密码;③:验证成功以后组装用户返回信息和处理redis中缓存的token信息。
在UserLoginController写登录接口:
package com.swh.server.controller;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.common.result.AjaxResult;
import com.swh.common.utils.JackJsonUtil;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
import com.swh.server.service.UserLoginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import static com.swh.common.constant.CommonConst.NotInterceptUrl.ADMIN_USER_LOGIN;
import static com.swh.common.constant.CommonConst.NotInterceptUrl.GENERATE_KEY;
import static com.swh.common.constant.CommonConst.SWH_TOKEN;
/**
* @ClassName: UserLoginController
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:33
*/
@Slf4j
@Api(tags = "登录")
@RestController
public class UserLoginController {
@Autowired
private UserLoginService userLoginService;
@ApiOperation(value = "获取token,用-分割,最后一节是用于加密的AES密钥")
@GetMapping(GENERATE_KEY)
public AjaxResult<String> getAesKey(@RequestHeader(value = SWH_TOKEN, required = false) String token) {
return AjaxResult.ok(userLoginService.getAesKey(token));
}
@ApiOperation("本地登录")
@PostMapping(ADMIN_USER_LOGIN)
public AjaxResult<RespAdminUserInfo> localUserLogin(@RequestBody ReqLocalUserLoginVo req, @RequestHeader(SWH_TOKEN) String token) {
LOGGER.info("本地登录,参数:{},token:{}", JackJsonUtil.obj2String(req), token);
return AjaxResult.ok(userLoginService.localUserLogin(req, token));
}
}
登录的具体实现逻辑:
UserLoginService
package com.swh.server.service;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
/**
* @ClassName: UserLoginService
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:35
*/
public interface UserLoginService {
/**
* 获取token
*
* @param token
* @return
*/
String getAesKey(String token);
/**
* 本地登录
*
* @param req
* @param token
* @return
*/
RespAdminUserInfo localUserLogin(ReqLocalUserLoginVo req, String token);
}
UserLoginServiceImpl
package com.swh.server.service.impl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import com.swh.admin.dao.AdminResDao;
import com.swh.admin.dao.AdminUserDao;
import com.swh.admin.dao.RoleResMpDao;
import com.swh.admin.dao.UserRoleMpDao;
import com.swh.admin.pojo.db.DbAdminResVo;
import com.swh.admin.pojo.db.DbAdminUserVo;
import com.swh.admin.pojo.resp.RespAdminResTreeVo;
import com.swh.admin.pojo.resp.RespAdminUserInfo;
import com.swh.admin.service.AdminResService;
import com.swh.common.exception.RequestException;
import com.swh.common.utils.*;
import com.swh.server.pojo.req.ReqLocalUserLoginVo;
import com.swh.server.service.UserLoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.swh.common.constant.AdminUserConst.MenuLevle.LEVLE_THREE;
import static com.swh.common.constant.CommonConst.*;
import static com.swh.common.constant.CommonConst.CacheKey.REDIS_ADMIN_USER_LOGIN;
import static com.swh.common.result.ResultCode.USERNAME_OR_PASSWORD_INVALID;
/**
* @ClassName: UserLoginServiceImpl
* @Description:
* @Author: songWenHao
* @Date: 2022/6/17 9:35
*/
@Slf4j
@Service
public class UserLoginServiceImpl implements UserLoginService {
@Autowired
private AdminUserDao adminUserDao;
@Autowired
private UserRoleMpDao userRoleMpDao;
@Autowired
private RoleResMpDao roleResMpDao;
@Autowired
private AdminResDao adminResDao;
@Autowired
private RedisCacheUtil redisCacheUtil;
@Autowired
private AdminResService adminResService;
@Override
public String getAesKey(String t) {
if (StrUtil.isBlank(t)) {
String key = RandomUtil.randomString(16);
LOGGER.debug("首次访问获取的AES key:{}", key);
String token = "swh-" + DbIdUtil.getInstance().nextUuid() + "-" + key;
redisCacheUtil.hashPut(token, LOGIN_STATUS, "false", SESSION_TIME, TimeUnit.MINUTES);
return token;
} else {
return t;
}
}
@Override
public RespAdminUserInfo localUserLogin(ReqLocalUserLoginVo req, String token) {
//验证用户名
DbAdminUserVo userVo = adminUserDao.getAdminUserByAccount(req.getAccount());
if (ObjectUtil.isNull(userVo)) {
throw new RequestException(USERNAME_OR_PASSWORD_INVALID);
}
//验证密码
String tmpPassword = SmUtil.sm3(req.getAccount() + DecryptPasswordUtil.e(req.getPassword()));
if (!StrUtil.equals(tmpPassword, userVo.getPassword())) {
throw new RequestException(USERNAME_OR_PASSWORD_INVALID);
}
return build(userVo, token);
}
/**
* 构建用户信息返回前端
*
* @param dbAdminUserVo
* @param token
* @return
*/
private RespAdminUserInfo build(DbAdminUserVo dbAdminUserVo, String token) {
//组装用户信息需要放置的信息
Map<String, String> mapLoginUserMap = MapUtil.newHashMap(5);
//获取用户关联的角色ID
List<String> listRoleIds = userRoleMpDao.getRoleIdsByUserId(dbAdminUserVo.getId());
// 根据角色获取关联的资源Id
List<String> listResIds = roleResMpDao.getResIdsByRoleIds(listRoleIds);
//根据资源Id获取所有的资源
List<DbAdminResVo> resultRes = adminResDao.getResourceByIds(listResIds);
//可访问的path 路径
List<String> resPath = resultRes.parallelStream().map(r -> {
if (ObjectUtil.isNotNull(r) && r.getLevel() == LEVLE_THREE) {
return r.getPath();
}
return null;
}).filter(Objects::nonNull).distinct().collect(Collectors.toList());
//可访问的资源ID
List<String> resCode = resultRes.parallelStream().map(r -> {
if (ObjectUtil.isNotNull(r) && r.getLevel() == LEVLE_THREE) {
return r.getModelCode() + r.getFunCode() + r.getOperCode();
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList());
//生成用户菜单
List<RespAdminResTreeVo> menu = adminResService.getUserMenuTree(resultRes);
//组装用户信息需要放置的信息
mapLoginUserMap.put(LOGIN_STATUS, "true");
mapLoginUserMap.put(ADMIN_RES_PATH, JackJsonUtil.obj2String(resPath));
mapLoginUserMap.put(ADMIN_USER_ID, dbAdminUserVo.getId());
mapLoginUserMap.put(ADMIN_USER_NAME, UrlUtil.encode(dbAdminUserVo.getUserName()));
mapLoginUserMap.put(ADMIN_USER_ACCOUNT, dbAdminUserVo.getAccount());
redisCacheUtil.hashPut(token, mapLoginUserMap, 30, TimeUnit.MINUTES);
return new RespAdminUserInfo(dbAdminUserVo, resCode, menu);
}
}