我分到的任务主要是登录模块,主要任务为:
1.登录模块(登录和忘记密码)
2.实现rbac模型
3.aop实现操作日志和权限验证
登录模块
1.1登录功能的实现
这个没什么好说的就是简单的查询数据库用户表里面的账号和密码是否对应的上
1.1.1xml
<select id="login" resultType="com.qcby.teaching.msgmanage.entity.vo.SysUserVo">
select * from sys_user
<where>
account = #{account,jdbcType=INTEGER} and password = #
{password,jdbcType=VARCHAR}
</where>
</select>
1.1.2Mapper层
SysUserVo login(@Param("account") Long account, @Param("password") String password);
1.1.3 Servicel和Serviclmpl
Service
//登陆
SysUserVo login(Long account,String password);
Servicelmpl:
//登陆
@Override
public SysUserVo login(Long account,String password) {
SysUserVo sysUserVo = loginMapper.login(account,password);
if(sysUserVo == null){
return null;
}
return sysUserVo;
}
1.1.4Controller:
controller层要弄很多东西 首先是token存值,获取登录日志,外加查询角色等等
@RequestMapping
public ResultJson login(@RequestBody SysUserVo sysUserVo, HttpSession session) throws UnknownHostException {
log.info("" + sysUserVo);
if (StringUtils.isEmpty(sysUserVo.getAccount()) || StringUtils.isEmpty(sysUserVo.getPassword())) {
return ResultJson.error("用户名或者密码为空!");
}
Long account = sysUserVo.getAccount();
String password = sysUserVo.getPassword();
log.info("登陆的账号和密码{}" + account + password);
SysUserVo status = loginService.login(account, password);
if (status != null) {
sysUserVo.setId(sysUserVo.getAccount());
// 生成token
String token = JwtUtil.createToken(sysUserVo.getId());
log.info("777777777" + sysUserVo.getId());
sysUserVo.setToken(token);
/**
* 打印登陆日志,最后再解开
*/
InetAddress addr = InetAddress.getLocalHost();
String ip = addr.getHostAddress();
LogLogin logLogin = new LogLogin();
logLogin.setAccount(account);
logLogin.setLoginTime(LocalDateTime.now());
logLogin.setIp(ip);
logLoginService.save(logLogin);
/**
* 角色查询,权限查询。。。。
*/
List<SysRole> roleList = adminSysUserService.getRoleById(status.getId());
sysUserVo.setRoleList(roleList);
log.info("你好" + token);
globalContext.addSysUser(sysUserVo);
return ResultJson.ok(sysUserVo);
}
//调用service获取用户信息
return ResultJson.error("账号名或者密码错误!");
}
1.2忘记密码功能实现(查询加修改)
这个我的逻辑是先查找账号和邮箱是否正确,当账号和邮箱正确后输入两次密码即可,当然这个还不够严谨,但是也是自己练习的一个小东西。
1.2.1xml
<!--验证邮箱-->
<select id="selectEmail" resultType="com.qcby.teaching.msgmanage.entity.vo.SysUserVo">
select * from sys_user
<where>
account = #{account,jdbcType=INTEGER} and email = #{email,jdbcType=VARCHAR}
</where>
</select>
<!--修改密码-->
<update id="updatePass"
parameterType="com.qcby.teaching.msgmanage.entity.vo.SysUserVo">
update sys_user
set password = #{password,jdbcType=VARCHAR}
<where>
account = #{account,jdbcType=INTEGER}
</where>
</update>
1.2.2Mapper层
//验证邮箱和账号是否正确
SysUserVo selectEmail(@Param("account") Long account, @Param("email") String email);
//修改密码
int updatePass(@Param("account") Long account, @Param("password") String password);
1.2.3 Servicel和Serviclmpl
Serivce
//验证邮箱和账号是否正确
Boolean selectEmail(Long account,String email);
//修改密码
int updatePass( Long account, String password);
Serivelmpl
//修改密码
@Override
public Boolean selectEmail(Long account, String email) {
SysUserVo sysUserVo = loginMapper.selectEmail(account,email);
if(sysUserVo==null){
return false;
}
return true;
}
//验证邮箱和账号是否正确
@Override
public int updatePass(Long account, String password) {
return loginMapper.updatePass(account,password);
}
1.2.4Controller:
/**
* 根据邮箱修改密码
*
* @param sysUserVo
* @param pwd
* @param newpwd
* @return
* @throws UnknownHostException
*/
@RequestMapping("updatePassword")
public ResultJson updatePass(SysUserVo sysUserVo, String pwd, String newpwd) throws UnknownHostException {
if (StringUtils.isEmpty(sysUserVo.getAccount()) || StringUtils.isEmpty(sysUserVo.getEmail())) {
return ResultJson.error("用户名或者邮箱号为空!");
}
Long account = sysUserVo.getAccount();
String email = sysUserVo.getEmail();
log.info("登陆的账号和密码{}" + account + email);
boolean update = loginService.selectEmail(account, email);
if (update) {
if (pwd.equals(newpwd)) {
loginService.updatePass(account, pwd);
return ResultJson.ok("修改成功");
} else {
return ResultJson.error("两次密码不一致");
}
}
return ResultJson.error("修改失败!");
}
1.2.5接口验证:
这是我之前的数据库字段:
咱们就来修改法外狂徒张三的密码吧
页面low了点,但是问题不大,点击确认,密码成功从“123456”到“123”
1.3token的存与取
token其实就是个字符串,用来验证登录后的令牌,同时能解析出存入的数据(我把它当成session使用了)
token原理:
token总代码:
public class JwtUtil {
//过期时间 30min
private static final int EXPIRE_TIME = 30;
//私钥
private static final String TOKEN_SECRET = "privateKey";
private static final String USER_NAME = "name";
/**
* 签发对象:这个用户的id
* 签发时间:现在
* 有效时间:30分钟
* 载荷内容:暂时设计为:这个人的名字
* 加密密钥:这个人的id加上一串字符串
* @param userId
* @return
*/
//创建token
public static String createToken(Long userId) {
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE,EXPIRE_TIME);
Date expiresDate = nowTime.getTime();
return JWT.create().withAudience(userId+"") //签发对象
.withIssuedAt(new Date()) //发行时间
.withExpiresAt(expiresDate) //有效时间
// .withClaim(USER_NAME, userName) //载荷,随便写几个都可以
.sign(Algorithm.HMAC256(userId+TOKEN_SECRET)); //加密
}
/**
* 检验合法性,其中secret参数就应该传入的是用户的id
* @param token
* @throws MsgmanageException
*/
public static void verifyToken(String token, String secret) throws MsgmanageException {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+TOKEN_SECRET)).build();
verifier.verify(token);
} catch (Exception e) {
//效验失败
//自定义的一个异常
throw new MsgmanageException(GlobalConstant.TOKEN_UNAVILABLE);
}
}
/**
* 获取签发对象
*/
public static String getAudience(String token) throws MsgmanageException {
String audience = null;
try {
audience = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
//这里是token解析失败
throw new MsgmanageException(GlobalConstant.TOKEN_UNAVILABLE);
}
return audience;
}
//上面生产token并且验证其合法性(有无超时等)
/**
* 自动获取用户Id
* @param request
* @return
*/
public static Long getUserId(HttpServletRequest request){
String token = CookieUtil.INSTANCE.getTokenFromCookie(request);
String userId = getAudience(token);
return userId == null ? null : Long.valueOf(userId);
}
/**
* 获取角色Id
* @param request
* @return
*/
public static SysRole getRole(HttpServletRequest request, GlobalContext globalContext){
Long userId = getUserId(request);
SysUserVo sysUserVo = globalContext.getSysUser(userId);
return sysUserVo.getRoleList().get(0);
}
public static void main(String[] args) {
String token = createToken(8L);
System.out.println("========================>"+token);
String userId = getAudience(token);
System.out.println("===================>"+userId);
}
}
项目token的步骤:
1.登录成功放入实体类中,返回给前台(实体类要有自定义的token变量)
//登录成功将userid放入token中(我项目里面放入的是账号方便关联后续的表) String token = JwtUtil.createToken(sysUserVo.getId()); sysUserVo.setToken(token);
2.前台从后台获取token并放入cookie里面
//获取到后台返回的token var token = data.data.token; //addCookie方法就是将前台的token放入cookie里面 addCookie("msgToken",token);
3.后台获取token
//获取登录的token String token = CookieUtil.INSTANCE.getTokenFromCookie(request); //getUserId方法解析token字符串 Long account = JwtUtil.getUserId(request);
CookieUtil类:
获取所有的cookie 遍历前台名字与后台定义的token名字一致的时候 就可以获取到登录认证的token了
public class CookieUtil {
// 单例模式
public static final CookieUtil INSTANCE = new CookieUtil();
private CookieUtil() { }
/**
* @Description: 从cookie获取token
* @param request
* @return: java.lang.String
* @Author: lxt
* @Date: 2021/7/5 16:56
*/
//获取所有的cookie 遍历前台名字与后台定义的token名字一致的时候 就可以获取到登录认证的token了
public String getTokenFromCookie(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals(GlobalConstant.TOKEN)){
return cookie.getValue();
}
}
return null;
}
public String getToken(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("code3")){
return cookie.getValue();
}
}
return null;
}
}
接上文登录模块
1.4获取登录用户角色信息
(如果只有一个角色,直接登录,如果有一个角色以上,则跳转到选角色页面,如果没有角色则显示该用户没有角色)
getRoleById方法就是根据用户ID获取角色信息
根据登录获取的用户id获取到他所有的角色信息,根据角色数量返回前台来判断是否进入选角色页面
选择角色按钮
//管理员
form.on("submit(role1)",function(data){
selectRole(1);
});
//教师
form.on("submit(role2)",function(data){
selectRole(2);
});
//学生
form.on("submit(role3)",function(data){
selectRole(3);
});
function selectRole(roleId){
var code = $("#code").val();
// ajax请求后台接口,选择角色
$.ajax({
url:"/rest/login/selectRole",
type:"post",
data:{
"roleId": roleId
},
success:function(data){
if(data.code == 0){
window.location.href = "/index";
}else{
layer.msg(data.msg);
}
}});
//跳转 index
}
html主要代码 根据后台获取的角色id返回前台展示有什么角色
<div class="layui-form-item" th:each="role:${roleList}"> <button class="layui-btn layui-block" th:onclick="selectRole([[${role.id}]])" lay-submit type="button">[[${role.roleName}]] </button> </div>
contller层筛选角色代码
选择角色点击角色筛选那个角色id
@RequestMapping("selectRole")
public ResultJson selectRole(HttpServletRequest request, Long roleId) {
// 用户id
Long userId = JwtUtil.getUserId(request);
SysUserVo sysUserVo = globalContext.getSysUser(userId);
// 根据角色id筛选出所选角色
List<SysRole> roleList = sysUserVo.getRoleList()
// 通过filter函数进行筛选
.stream().filter(r -> r.getId().equals(roleId))
.collect(Collectors.toList());
// 重置上下文中的角色信息
sysUserVo.setRoleList(roleList);
return ResultJson.ok();
}
1.5 登录日志
//获取本机ip
InetAddress addr = InetAddress.getLocalHost();
String ip = addr.getHostAddress();
封装
LogLogin logLogin = new LogLogin();
logLogin.setAccount(account);
logLogin.setLoginTime(LocalDateTime.now());
logLogin.setIp(ip);
插入登录日志表
logLoginService.save(logLogin);