登录和退出登录接口设计(基于jwt token 和redis)
1. 登录
1.1 接口说明
接口url:/login
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
account | string | 账号 |
password | string | 密码 |
返回数据:
1 2 3 4 5 6 | { "success" : true , "code" : 200 , "msg" : "success" , "data" : "token" } |
1.2 JWT
登录使用JWT技术。
jwt 可以生成 一个加密的token,做为用户登录的令牌,当用户登录成功之后,发放给客户端。
请求需要登录的资源或者接口的时候,将token携带,后端验证token是否合法。
jwt 有三部分组成:A.B.C
A:Header,{“type”:“JWT”,“alg”:“HS256”} 固定
B:playload,存放信息,比如,用户id,过期时间等等,可以被解密,不能存放敏感信息
C: 签证,A和B加上秘钥 加密而成,只要秘钥不丢失,可以认为是安全的。
jwt 验证,主要就是验证C部分 是否合法。
依赖包:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
工具类:
package com.mszlu.blog.utils; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JWTUtils { private static final String jwtToken = "123456Mszlu!@#$$"; public static String createToken(Long userId){ Map<String,Object> claims = new HashMap<>(); claims.put("userId",userId); JwtBuilder jwtBuilder = Jwts.builder() .signWith(SignatureAlgorithm.HS256, jwtToken) // 签发算法,秘钥为jwtToken .setClaims(claims) // body数据,要唯一,自行设置 .setIssuedAt(new Date()) // 设置签发时间 .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000));// 一天的有效时间 String token = jwtBuilder.compact(); return token; } public static Map<String, Object> checkToken(String token){ try { Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token); return (Map<String, Object>) parse.getBody(); }catch (Exception e){ e.printStackTrace(); } return null; } }
1.3 Controller
package com.mszlu.blog.controller; import com.mszlu.blog.service.LoginService; import com.mszlu.blog.vo.Result; import com.mszlu.blog.vo.params.LoginParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("login") public class LoginController { @Autowired private LoginService loginService; @PostMapping public Result login(@RequestBody LoginParam loginParam){ return loginService.login(loginParam); } }
1.4 Service
package com.mszlu.blog.service; import com.mszlu.blog.vo.Result; import com.mszlu.blog.vo.params.LoginParam; public interface LoginService { /** * 登录 * @param loginParam * @return */ Result login(LoginParam loginParam); }
md5加密的依赖包:
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency>
package com.mszlu.blog.service.impl; import com.alibaba.fastjson.JSON; import com.mszlu.blog.dao.pojo.SysUser; import com.mszlu.blog.service.LoginService; import com.mszlu.blog.service.SysUserService; import com.mszlu.blog.utils.JWTUtils; import com.mszlu.blog.vo.ErrorCode; import com.mszlu.blog.vo.Result; import com.mszlu.blog.vo.params.LoginParam; 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.RedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class LoginServiceImpl implements LoginService { private static final String slat = "mszlu!@#"; @Autowired private SysUserService sysUserService; @Autowired private RedisTemplate<String, String> redisTemplate; @Override public Result login(LoginParam loginParam) { String account = loginParam.getAccount(); String password = loginParam.getPassword(); if (StringUtils.isBlank(account) || StringUtils.isBlank(password)){ return Result.fail(ErrorCode.PARAMS_ERROR.getCode(),ErrorCode.PARAMS_ERROR.getMsg()); } String pwd = DigestUtils.md5Hex(password + slat); SysUser sysUser = sysUserService.findUser(account,pwd); if (sysUser == null){ return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(),ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg()); } //登录成功,使用JWT生成token,返回token和redis中 String token = JWTUtils.createToken(sysUser.getId()); redisTemplate.opsForValue().set("TOKEN_"+token, JSON.toJSONString(sysUser),1, TimeUnit.DAYS); return Result.success(token); } public static void main(String[] args) { System.out.println(DigestUtils.md5Hex("admin"+slat)); } }
@Override public SysUser findUser(String account, String pwd) { LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getAccount,account); queryWrapper.eq(SysUser::getPassword,pwd); queryWrapper.select(SysUser::getId,SysUser::getAccount,SysUser::getAvatar,SysUser::getNickname); queryWrapper.last("limit 1"); SysUser sysUser = sysUserMapper.selectOne(queryWrapper); return sysUser; }
SysUser findUser(String account, String pwd);
package com.mszlu.blog.vo.params; import lombok.Data; @Data public class LoginParam { private String account; private String password; }
spring.redis.host=localhost
spring.redis.port=6379
package com.mszlu.blog.vo; public enum ErrorCode { PARAMS_ERROR(10001,"参数有误"), ACCOUNT_PWD_NOT_EXIST(10002,"用户名或密码不存在"), NO_PERMISSION(70001,"无访问权限"), SESSION_TIME_OUT(90001,"会话超时"), NO_LOGIN(90002,"未登录"),; private int code; private String msg; ErrorCode(int code, String msg){ this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
2. 获取用户信息
2.1 接口说明
接口url:/users/currentUser
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
Authorization | string | 头部信息(TOKEN) |
返回数据:
{ "success": true, "code": 200, "msg": "success", "data": { "id":1, "account":"1", "nickaname":"1", "avatar":"ss" } }
2.2 Controller
package com.mszlu.blog.controller; import com.mszlu.blog.service.SysUserService; import com.mszlu.blog.vo.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("users") public class UserController { @Autowired private SysUserService sysUserService; @GetMapping("currentUser") public Result currentUser(@RequestHeader("Authorization") String token){ return sysUserService.getUserInfoByToken(token); } }
2.3 Service
Result getUserInfoByToken(String token);
@Override public Result getUserInfoByToken(String token) { Map<String, Object> map = JWTUtils.checkToken(token); if (map == null){ return Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg()); } String userJson = redisTemplate.opsForValue().get("TOKEN_" + token); if (StringUtils.isBlank(userJson)){ return Result.fail(ErrorCode.NO_LOGIN.getCode(),ErrorCode.NO_LOGIN.getMsg()); } SysUser sysUser = JSON.parseObject(userJson, SysUser.class); LoginUserVo loginUserVo = new LoginUserVo(); loginUserVo.setAccount(sysUser.getAccount()); loginUserVo.setAvatar(sysUser.getAvatar()); loginUserVo.setId(sysUser.getId()); loginUserVo.setNickname(sysUser.getNickname()); return Result.success(loginUserVo); }
2.4 LoginUserVo
package com.mszlu.blog.vo; import lombok.Data; @Data public class LoginUserVo { private Long id; private String account; private String nickname; private String avatar; }
3. 退出登录
3.1 接口说明
接口url:/logout
请求方式:GET
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
Authorization | string | 头部信息(TOKEN) |
返回数据:
{ "success": true, "code": 200, "msg": "success", "data": null }
3.2 Controller
package com.mszlu.blog.controller; import com.mszlu.blog.service.LoginService; import com.mszlu.blog.vo.Result; import com.mszlu.blog.vo.params.LoginParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("logout") public class LogoutController { @Autowired private LoginService loginService; @GetMapping public Result logout(@RequestHeader("Authorization") String token){ return loginService.logout(token); } }
3.3 Service
@Override public Result logout(String token) { redisTemplate.delete("TOKEN_"+token); return Result.success(null); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人