当前位置: 首页 > news >正文

后台管理系统的通用权限解决方案(十四)基于JWT实现登录功能

基于JWT实现登录功能

说明:以下代码仅仅是主体代码,其中涉及的实体类、码表、工具类等没有贴出来,完整代码可以在本篇文章中直接下载。

  • 1)在LoginController中创建doLogin方法
// com.itweid.auth.controller.LoginController@PostMapping("/doLogin")
@ApiOperation("用户登录")
public BaseResult<LoginVO> doLogin(@Validated @RequestBody LoginQuery loginQuery) {return loginService.doLogin(loginQuery);
}
  • 2)创建ILoginService接口及LoginServiceImpl实现类,在实现类在实现doLogin方法
package com.itweid.auth.service.impl;import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.itweid.auth.mapper.AuthResourceMapper;
import com.itweid.auth.service.ILoginService;
import com.itweid.common.code.ErrorCode;
import com.itweid.common.code.RedisCode;
import com.itweid.common.entity.AuthResource;
import com.itweid.common.entity.AuthUser;
import com.itweid.common.pojo.*;
import com.itweid.jwt.pojo.JwtUserInfo;
import com.itweid.jwt.utils.AuthTokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class LoginServiceImpl implements ILoginService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate AuthTokenUtils authTokenUtils;@Autowiredprivate AuthResourceMapper authResourceMapper;@Overridepublic BaseResult<LoginVO> doLogin(LoginQuery loginQuery) {log.info(loginQuery.toString());// 校验验证码是否正确BaseResult checkCodeRes = checkCode(loginQuery.getKey(), loginQuery.getCode());if(!checkCodeRes.isSuccess()) {return BaseResult.setError(checkCodeRes);}// 校验账号、密码是否正确BaseResult<AuthUser> checkPassRes = checkPass(loginQuery.getAccount(), loginQuery.getPassword());if(!checkPassRes.isSuccess()) {return BaseResult.setError(checkPassRes);}// 为用户生成jwt令牌AuthUser authUser = checkPassRes.getData();Token token = generateUserToken(authUser);// 查询当前用户可以访问的资源权限List<AuthResource> authResourceList = authResourceMapper.findVisibleResourceByUserId(authUser.getId());List<String> permissionList = null;if(authResourceList != null && !authResourceList.isEmpty()) {// 用户对应的权限(给前端使用的)permissionList = authResourceList.stream().map(AuthResource::getCode).collect(Collectors.toList());// 将用户对应的权限(给后端网关使用的)进行缓存List<String> visibleResource = authResourceList.stream().map((resource -> {return resource.getMethod() + resource.getUrl();})).collect(Collectors.toList());stringRedisTemplate.opsForHash().put(RedisCode.USER_RESOURCE, authUser.getId().toString(), visibleResource.toString());}// 封装返回结果UserVO userVO = new UserVO();BeanUtil.copyProperties(authUser, userVO);LoginVO loginDTO = LoginVO.builder().user(userVO).token(token).permissionsList(permissionList).build();return BaseResult.setOk(loginDTO);}private BaseResult checkCode(String key, String code) {// 从缓存中获取验证码并进行验证Object captchaObj = stringRedisTemplate.opsForHash().get(RedisCode.CAPTCHA, key);log.info("Redis => get {} {} => {}", RedisCode.CAPTCHA, key, captchaObj);if (captchaObj == null) {return BaseResult.setError(ErrorCode.VALID_CODE_IS_OVERDUE);}if (!StringUtils.equalsIgnoreCase(code, String.valueOf(captchaObj))) {return BaseResult.setError(ErrorCode.VALID_CODE_IS_WRONG);}// 验证通过,立即从缓存中删除验证码Long delete = stringRedisTemplate.opsForHash().delete(RedisCode.CAPTCHA, key);log.info("Redis => del {} {} => {}", RedisCode.CAPTCHA, key, delete);return BaseResult.setOk();}public BaseResult<AuthUser> checkPass(String account, String password) {AuthUser authUser = Db.lambdaQuery(AuthUser.class).eq(AuthUser::getAccount, account).one();// 将前端提交的密码进行md5加密String md5Hex = DigestUtils.md5Hex(password);if (authUser == null || !authUser.getPassword().equals(md5Hex)) {// 认证失败return BaseResult.setError(ErrorCode.ACCOUNT_PASS_IS_WRONG);}// 认证成功return BaseResult.setOk(authUser);}private Token generateUserToken(AuthUser authUser){JwtUserInfo jwtUserInfo = new JwtUserInfo(authUser.getId(), authUser.getAccount());return authTokenUtils.generateUserToken(jwtUserInfo, null);}
}

由以上代码可知,实现登录的逻辑主要有以下几步:

  • 校验验证码是否正确:从缓存中获取已经保存好的验证码并进行验证,验证失败则返回登录失败,验证通过则立即从缓存中删除验证码。

  • 校验账号、密码是否正确:根据账号从数据库查询对应的用户信息,并进行密码验证,验证失败则返回登录失败。

  • 为用户生成jwt令牌:根据用户ID、用户账号生产JWT令牌,具体的生成逻辑可以参考:后台管理系统的通用权限解决方案(九)SpringBoot整合jjwt实现登录认证鉴权

  • 查询当前用户可以访问的资源权限:根据用户ID查询出该用户可以访问的资源权限,并保存到缓存中,同时返回前端。

  • 3)在AuthResourceMapper中创建findVisibleResourceByUserId方法,并在对应的AuthResourceMapper.xml中写SQL语句

@Mapper
public interface AuthResourceMapper extends BaseMapper<AuthResource> {List<AuthResource> findVisibleResourceByUserId(Long userId);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itweid.auth.mapper.AuthResourceMapper"><!-- 通用查询结果列 --><sql id="Base_Column_List">id, create_user, create_time, update_user, update_time,code, name, menu_id, describe_, method, url</sql><select id="findVisibleResourceByUserId" resultType="AuthResource">select <include refid="Base_Column_List"/>from t_auth_resource<where>id in (select authority_id FROM t_auth_role_authority rainner join t_auth_user_role ur on ra.role_id = ur.role_idinner join t_auth_role r on r.id = ra.role_idwhere ur.user_id = #{userId, jdbcType=BIGINT} and r.`status` = trueand ra.authority_type = 'RESOURCE')</where></select>
</mapper>
  • 4)启动项目,访问接口文档http://127.0.0.1:8081/doc.html

  • 5)在接口文档中调试登录功能

可见,前端已经获取到了用户信息、token信息、以及可使用的权限信息,后续前端向后端发起任何请求时都需要携带token,还可以使用权限信息控制页面的显示。

本节完,更多内容查阅:后台管理系统的通用权限解决方案


http://www.mrgr.cn/news/74657.html

相关文章:

  • ubontu--cuDNN安装
  • 推理还原的干货
  • mongoDB的安装及使用
  • LeetCode 3.无重复字符的最长子串
  • TofuAI处理BT1120时序视频要求
  • 深度学习:tensor的定义与维度
  • 电路板维修入门之集成电路的检测方法篇
  • 苹果低价版Vision Pro 推迟至2027年发布:XR领域的变局与挑战
  • 【Oracle篇】掌握SQL Tuning Advisor优化工具:从工具使用到SQL优化的全方位指南(第六篇,总共七篇)
  • 开发指南079-数据冗余
  • Java 中的字符输入流详解
  • Vue3 常见的 9 种组件通信机制
  • SpringBoot开发——整合OpenCSV 实现数据导入导出-CSV
  • 《.addClass()》
  • 【Hive】【HiveQL】【大数据技术基础】 作业三 数据仓库Hive的使用
  • 107、Python并发编程:失败自动重试,一次搞懂简单实用的Timer
  • 网络安全开发详解与python实现
  • 69页可编辑PPT | 大数据基础知识培训课件
  • 系统架构设计师论文
  • 对于目标文件太大无法拉入u盘事件的解决方法
  • 关于我发布了第一篇vip文章这件事
  • 寻宝--Kruskal
  • 缓存雪崩问题及解决方法
  • 解决 VMware 虚拟机找不到共享文件夹
  • scp 或 ssh 报错no matching host key type found. Their offer: ssh-rsa 解决方案
  • 07Linux操作命令