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

【方案实践】公寓租赁项目(十):基于SpringBoot登录管理接口构建

目录

登录接口说明:LoginController

一、获取图形验证码接口

controller层:

service层:

二、登录接口

controller层:

service层:

mapper层:

三、JwtUtil创建

四、获取登陆用户个人信息接口

controller层:

(重要)因为我们解析token的方法是在拦截器里调用,所以如果我们每一次获取登录者的时候,要获取token进行解析,连续调用两次parseToken()方法就不太合理了。所以为避免重复解析,通常会在拦截器将Token解析完毕后,将结果保存至ThreadLocal中,这样一来,我们便可以在整个请求的处理流程中进行访问了。

修改拦截器:

service层:


登录接口说明:LoginController

@Tag(name = "后台管理系统登录管理")
@RestController
@RequestMapping("/admin")
public class LoginController {@Autowiredprivate LoginService service;
}

一、获取图形验证码接口

接口名称:getCaptcha

请求方式:Get

请求路径:/admin/login/captcha

请求参数:无

返回类型:CaptchaVo

我们先查看CaptchaVo有哪些属性:

@Data
@Schema(description = "图像验证码")
@AllArgsConstructor
public class CaptchaVo {@Schema(description="验证码图片信息")private String image;@Schema(description="验证码key")private String key;
}

需要获取验证码图片的信息和存储在redis里的key。那么怎么生成验证码呢?我们需要使用开源的验证码生成工具EasyCaptcha。

导入maven依赖:

com.github.whvcseeasy-captcha

controller层:

@Operation(summary = "获取图形验证码")@GetMapping("login/captcha")public Result getCaptcha() {CaptchaVo result= service.getCaptcha();return Result.ok(result);}

service层:

public CaptchaVo getCaptcha() {SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);  //分别设置验证码区域的长、宽、以及验证码长度。String code = specCaptcha.text().toLowerCase();  //把验证码转成小写String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID(); //遵循命名规范stringRedisTemplate.opsForValue().set(key,code,RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);  //把key和验证码值存入redisreturn new CaptchaVo(specCaptcha.toBase64(),key);}

本项目Reids中的key需遵循以下命名规范:项目名:功能模块名:其他,例如admin:login:123456


二、登录接口

接口名称:login

请求方式:Post

请求路径:/admin/login

请求参数:@RequestBody LoginVo loginVo

返回类型:Result<String>

controller层:

@Operation(summary = "登录")@PostMapping("login")public Result login(@RequestBody LoginVo loginVo) {String jwt=service.login(loginVo);return Result.ok(jwt);}

loginVo用来封装前端登录界面用户提交的用户名、密码、验证码以及验证码在redis的key

@Data
@Schema(description = "后台管理系统登录信息")
public class LoginVo {@Schema(description="用户名")private String username;@Schema(description="密码")private String password;@Schema(description="验证码key")private String captchaKey;@Schema(description="验证码code")private String captchaCode;
}

service层:

public String login(LoginVo loginVo) {if (loginVo.getCaptchaCode()==null){  //判断输入的验证码是否为空throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND);}String code = stringRedisTemplate.opsForValue().get(loginVo.getCaptchaKey());  //从redis中获取codeif (code==null){  //判断查询的code是否为空throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);}if (!code.equals(loginVo.getCaptchaCode().toLowerCase())){  //如果code与输入的code的小写相等throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);}SystemUser systemUser = systemUserMapper.selectOneByUsername(loginVo.getUsername());  //通过用户名查询用户if (systemUser==null){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR);}if (systemUser.getStatus()== BaseStatus.DISABLE){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);}if (!systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);}return JwtUtil.createToken(systemUser.getId(),systemUser.getUsername());  //返回token}

mapper层:

自定义通过用户名字查询用户的方法。

 

三、JwtUtil创建

JwtUtil是用来生成token来识别登录的用户是否合法,如果合法则从数据库中查询用户信息,并响应给前端。而且后续前端想要请求后端接口也需要解析携带的token。

导入依赖:

io.jsonwebtokenjjwt-api

io.jsonwebtokenjjwt-implruntime

io.jsonwebtokenjjwt-jacksonruntime
public class JwtUtil {private static long tokenExpiration = 60 * 60 * 1000L;private static SecretKey tokenSignKey = Keys.hmacShaKeyFor("M0PKKI6pYGVWWfDZw90a0lTpGYX1d4AQ".getBytes());/*** 创建token方法* @param userId* @param username* @return*/public static String createToken(Long userId, String username) {String token = Jwts.builder().setSubject("LOGIN_USER").setExpiration(new Date(System.currentTimeMillis() + 3600000)).claim("userId", userId).claim("username", username).signWith(tokenSignKey, SignatureAlgorithm.HS256).compact();return token;}/*** 解析token方法* @param token* @return*/public static Claims parseToken(String token){if (token==null){throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);}try {JwtParser jwtParser = Jwts.parser().setSigningKey(tokenSignKey).build();Jws claimsJws = jwtParser.parseClaimsJws(token);Claims claims = claimsJws.getBody();return claims;}catch (ExpiredJwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);}catch (JwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);}}

四、获取登陆用户个人信息接口

接口名称:info

请求方式:Get

请求路径:/admin/info

请求参数:无

返回类型:Result<SystemUserInfoVo>

controller层:

返回的类型为VO对象,需要自己定义查询方法。

重要)因为我们解析token的方法是在拦截器里调用,所以如果我们每一次获取登录者的时候,要获取token进行解析,连续调用两次parseToken()方法就不太合理了。所以为避免重复解析,通常会在拦截器将Token解析完毕后,将结果保存至ThreadLocal中,这样一来,我们便可以在整个请求的处理流程中进行访问了。

ThreadLocal的主要作用是为每个使用它的线程提供一个独立的变量副本,使每个线程都可以操作自己的变量,而不会互相干扰,其用法如下图所示。

每个ThreadLocal对象都拥有set、get、remove方法,我们只需要编写逻辑调用即可自定义存储、获取、清理本地线程对象的方法。

public class LoginUserHolder {public static ThreadLocal threadLocal = new ThreadLocal<>();  //创建本地线程对象public static void setLoginUser(LoginUser loginUser) {threadLocal.set(loginUser);   //把登录用户信息存储进本地线程对象}public static LoginUser getLoginUser() {return threadLocal.get();  //获取登录用户信息}public static void clear() {threadLocal.remove();  //清理存储的用户信息}
}
@Operation(summary = "获取登录用户个人信息")@GetMapping("info")public Result info() {Long userId = LoginUserHolder.getLoginUser().getUserId();SystemUserInfoVo systemUserInfoVo=service.getLoginUserInfoById(userId);return Result.ok(systemUserInfoVo);}

修改拦截器:

@Component
public class AuthenticationIntercepter implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("access-token");Claims claims = JwtUtil.parseToken(token);Long userId = claims.get("userId", Long.class);String username = claims.get("username", String.class);LoginUserHolder.setLoginUser(new LoginUser(userId, username));  //封装登录对象存储进本地线程对象return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginUserHolder.clear();  //清理本地线程方法}
}

service层:

public SystemUserInfoVo getLoginUserInfoById(Long userId) {SystemUser systemUser = systemUserMapper.selectById(userId);SystemUserInfoVo systemUserInfoVo = new SystemUserInfoVo();systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());systemUserInfoVo.setName(systemUser.getName());return systemUserInfoVo;}

http://www.jsqmd.com/news/362603/

相关文章:

  • 白帽谷歌seo快速排名外链哪里有?真实渠道、方法和避坑全讲清
  • 2026年实测上海新工厂规划精实工业信息技术 - 孟哥商业圈
  • 深入剖析大数据领域的数据清洗需求
  • iOS 开发助手,性能测试、实时日志、应用管理、设备信息查看
  • 3小时搞定万字综述?2026年论文写作工具红黑榜:第一名堪称全能“学术外挂” - 沁言学术
  • 软考一次过的概率大吗?看完通过率分析,你就明白了!
  • 百亿积分泡沫破裂!新一轮“绿色积分”靠什么让用户争相买单?
  • 内存计算技术在大数据分析中的7个关键应用
  • 2026国自然模板大改,无从下笔?
  • 从PLY到3DTiles:GISBox助力三维数据格式转换全流程 - 详解
  • 别学 Prompt 了!AI 原生时代,Context Engineering 才是饭碗
  • AI应用架构师必看:企业智能体系统架构的模型监控策略
  • arm架构能装windows吗?arm架构安装Windows两种方法
  • ET交易员采访|技术分析不再用来预测,而是用来约束自己
  • CANN高性能集合通信库HCCL的架构设计与分布式训练优化技术解析
  • APP广告变现:如何根据产品特性与用户场景配置广告形式
  • 2026年2月四川KTV设备回收厂家权威推荐榜 - 深度智识库
  • 虚拟主播核心技术解析:动作捕捉与实时渲染软件的协同之道
  • 1998-2024年各省固定资产投资价格指数
  • 决胜国自然:专业PPT与配图,如何为您的申报“画龙点睛”
  • 常见ADC解析
  • 提示工程:分布式缓存策略的实战应用
  • 大数据领域 HDFS 数据存储成本优化策略
  • 什么是等保合规
  • 从浙超到城市联赛,二十二科技再助浙江足球!以营销枢纽助力企业赢在生意赛场
  • 如何选择高防服务器、高防CDN或高防IP
  • 当AI学会“听诊”:心肺听诊分析系统,正在悄悄改变医疗
  • P3406 海底高铁
  • ABC444E
  • 什么是医疗器械工业设计?2026三大趋势引领行业新纪元! - 匠言榜单