实战篇03:登录接口
一、登录接口信息
1.1 基本信息
请求路径:/user/login
请求方式:POST
接口描述:该接口用于登录
1.2 请求参数
请求参数格式:x-www-form-urlencoded
请求参数说明:
参数名称 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
username | 用户名 | string | 是 | 5~16位非空字符 |
password | 密码 | string | 是 | 5~16位非空字符 |
请求数据样例:
username=zhangsan&password=123456
1.3 响应数据
响应数据类型:application/json
响应参数说明:
名称 | 类型 | 是否必须 | 默认值 | 备注 | 其他信息 |
---|---|---|---|---|---|
code | number | 必须 | 响应码, 0-成功,1-失败 | ||
message | string | 非必须 | 提示信息 | ||
data | string | 必须 | 返回的数据,jwt令牌 |
响应数据样例:
{
"code": 0,
"message": "操作成功",
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjUsInVzZXJuYW1lIjoid2FuZ2JhIn0sImV4cCI6MTY5MzcxNTk3OH0.pE_RATcoF7Nm9KEp9eC3CzcBbKWAFOL0IsuMNjnZ95M"
}
1.4 备注说明
用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,浏览器都需要在请求头header中携带到服务端,请求头的名称为 Authorization,值为 登录时下发的JWT令牌。
如果检测到用户未登录,则http响应状态码为401
二、业务流程
- Controller进行Post登录校验
- 根据用户名查询User
- 判断用户名是否正确
- 判断密码是否正确
- Service曾和Mapper曾提供对应的方法供Controller使用(可以复用注册,已完成)
三、编写登录Controller
- 在UserController类中新增login方法,实现登录功能
package com.example.bigevent.controller;
import com.example.bigevent.pojo.Result;
import com.example.bigevent.pojo.User;
import com.example.bigevent.service.UserService;
import jakarta.validation.constraints.Pattern;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 查询用户名是否被占用
User u = userService.findByUserName(username);
if (u==null){
// 没有占用,进行注册
userService.register(username,password);
return Result.success();
}else {
return Result.error("用户名已被占用");
}
}
@PostMapping("/login")
public Result login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 根据用户名查询用户
User loginUser = userService.findByUserName(username);
// 判断用户是否存在
if(loginUser==null){
return Result.error("用户名错误");
}
// 判断密码是否正确
String md5String = DigestUtils.md5Hex(password);
if(loginUser.getPassword().equals(md5String)){
// 登陆成功
return Result.success("jwt token 令牌");
}
return Result.error("密码错误");
}
}
四、功能测试
- 使用postman访问/user/login进行功能测试
五、登录认证
- 新建ArticleController类,用于测试访问该接口前必须登录
package com.example.bigevent.controller;
import com.example.bigevent.pojo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/article")
public class ArticleController {
@GetMapping("/list")
public Result list(){
return Result.success("所有文章数据...");
}
}
- 引入JWT依赖
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
- 在测试中生成JWT令牌
package com.example.bigevent;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@SpringBootTest
class BigEventApplicationTests {
@Test
void contextLoads() {
}
@Test
public void testJWT(){
Map<String, Object>claims = new HashMap<>();
claims.put("id",1);
claims.put("username","xxx");
String token = JWT.create().withClaim("user", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256("qwe"));
System.out.println(token);
}
}
- 在测试中验证JWT令牌
@Test
public void testJWTParse(){
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6Inh4eCJ9LCJleHAiOjE3MTIwODQ0MzF9.KSd3aq2frdPjUwnyU-uFoC_MG6gB5wId7OTmPdWG9hA";
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("qwe")).build();
// 进行验证
DecodedJWT decodedJWT = jwtVerifier.verify(token);
Map<String, Claim> claims = decodedJWT.getClaims();
System.out.println(claims.get("user"));
}
- 生成JWT工具类:JwtUtil
package com.example.bigevent.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {
private static final String KEY = "itheima";
//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256(KEY));
}
//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}
- 在UserController中登录功能生成JWT token
@PostMapping("/login")
public Result login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 根据用户名查询用户
User loginUser = userService.findByUserName(username);
// 判断用户是否存在
if(loginUser==null){
return Result.error("用户名错误");
}
// 判断密码是否正确
String md5String = DigestUtils.md5Hex(password);
if(loginUser.getPassword().equals(md5String)){
// 登陆成功
Map<String,Object> claims = new HashMap<>();
claims.put("id",loginUser.getId());
claims.put("username",loginUser.getUsername());
String token = JwtUtil.genToken(claims);
return Result.success(token);
}
return Result.error("密码错误");
}
}
- 根据接口文档1.4备注说明,在ArticleController类中添加验证JWT token行为,实现访问功能函数前需要登陆
- 在请求头中获取对应数据,应该使用注解:@RequestHeader(name = “Authorization”)
- 使用HttpServletResponse response对象设置返回状态码
六、认证测试
- 登录账户,获取JWT字符串
- 在postman中使用get请求,header中添加token字符串
七、使用拦截器实现登录认证
- 创建拦截器包:interceptors,创建拦截器类:LoginInterceptor
package com.example.bigevent.interceptors;
import com.example.bigevent.pojo.Result;
import com.example.bigevent.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 令牌验证
String token = request.getHeader("Authorization");
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
// 拦截器放行
return true;
}catch (Exception e){
response.setStatus(401);
return false;
}
}
}
- 注册拦截器,创建包:config,创建类:WebConfig
package com.example.bigevent.config;
import com.example.bigevent.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 放行登录和注册接口
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}
- 删除ArticleController中的验证代码
package com.example.bigevent.controller;
import com.example.bigevent.pojo.Result;
import com.example.bigevent.utils.JwtUtil;
import jakarta.servlet.http.HttpServletResponse;
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;
import java.util.Map;
@RestController
@RequestMapping("/article")
public class ArticleController {
@GetMapping("/list")
public Result list(/*@RequestHeader(name = "Authorization") String token, HttpServletResponse response*/){
return Result.success("所有文章数据...");
// // 验证token
// try {
// Map<String, Object> claims = JwtUtil.parseToken(token);
// return Result.success("所有文章数据...");
// }catch (Exception e){
// response.setStatus(401);
// return Result.error("未登录");
// }
}
}