前置背景
Result类
:
package com.example.day724test.Dao;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
private Integer code;//业务状态码 0-成功 1-失败
private String message;//提示信息
private T data;//响应数据
//快速返回操作成功响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(0, "操作成功", data);
}
//快速返回操作成功响应结果
public static Result success() {
return new Result(0, "操作成功", null);
}
public static Result error(String message) {
return new Result(1, message, null);
}
}
Mapper:
Service:
Controler:
@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam("username") String username, @RequestParam("password") String password) {
try {
SysUser user = userService.get_by_name(username);
if (user == null || !user.getPassword().equals(password)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
return ResponseEntity.ok().body(user);
} catch (Exception e) {
log.error("登录异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器内部错误");
}
}
/*
添加一个查看所有的用户的信息方法去验证token相关
*/
@GetMapping("/all")
public ResponseEntity<?> getAllUser() {
try {
List<SysUser> users = userService.get_all();
return ResponseEntity.ok().body(users);
} catch (Exception e) {
log.error("获取用户列表异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器内部错误");
}
}
测试一下是否成功:
login接口:
all接口:
测试成功:步入正题
JSON Web Tokens (JWT) 和拦截器实现登录验证的详细步骤:
. 用户登录
- 用户提交用户名和密码到服务器。
- 服务器验证用户凭证。
- 如果验证成功,服务器生成一个 JWT 并将其返回给客户端。
引入jwt依赖项:这里使用的是版本低一些的
创建JwtUtil类:
//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
//.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 ))
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))
.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();
}
测试一波:
生成token:
将生成的token复制准备验证:
经过验证可以使用去usercontroller实现登录验证:
原来的登录代码:
@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam("username") String username, @RequestParam("password") String password) {
try {
SysUser user = userService.get_by_name(username);
if (user == null || !user.getPassword().equals(password)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
return ResponseEntity.ok().body(user);
} catch (Exception e) {
log.error("登录异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器内部错误");
}
}
导入JwtUtil包的token验证代码:
@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam("username") String username, @RequestParam("password") String password) {
try {
SysUser user = userService.get_by_name(username);
if (user == null || !user.getPassword().equals(password)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
/*
登录成功之后的方法内容
*/
Map<String,Object> claims=new HashMap<>();
claims.put("username",user.getUsername());
claims.put("password",user.getPassword());
String token= JwtUtil.genToken(claims);
log.info("登录成功, 用户名: {}", user.getUsername());
return ResponseEntity.ok().body(token);
/*
在登录接口生成了token
*/
/*
其他的接口需要验证token
*/
} catch (Exception e) {
log.error("登录异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器内部错误");
}
}
查看是否返回了我们所需要的token信息:
接下来使得其它的接口验证token:验证成功返回数据 否则错误
原来的/user/all:
@GetMapping("/all")
public ResponseEntity<?> getAllUser() {
try {
List<SysUser> users = userService.get_all();
return ResponseEntity.ok().body(users);
} catch (Exception e) {
log.error("获取用户列表异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器内部错误");
}
}
现在的接受token验证登录信息:
@GetMapping("/all")
public Result<String> getAllUser(@RequestHeader("Authorization") String token, HttpServletRequest response){ //请求头中获取token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
return Result.success("所有的文章数据");
}catch (Exception e){
return Result.error("未登录");
}
}
/*
在请求头信息中获取token
*/
测试:没有加token的时候
显然在postman测试请求头里面没有携带token所以提示未登录
携带token之后:
请求成功:这便完成了基本的登录检验
但是如果有多个接口 开发大工程项目势必过于繁琐 ,那么我们就使用更加抽象性的方法:使用拦截器
拦截器原理:
拦截器(Interceptors)是一种设计模式,用于在请求到达目标之前或响应离开之后执行某些操作。这种模式在多种编程环境和技术栈中都有应用,包括但不限于HTTP客户端和服务端、数据库访问层、消息队列等。
### 拦截器的基本原理
1. **注册拦截器**:
- 在应用程序启动时或者某个配置阶段,你需要注册一个或多个拦截器。
- 每个拦截器通常定义了两个方法:一个用于处理请求前的操作,另一个用于处理响应后的操作。2. **请求处理**:
- 当一个请求被发送时,它会先经过一系列预先注册的拦截器。
- 每个拦截器可以对请求进行一些预处理,比如修改请求头、添加日志记录、验证认证信息等。
- 如果某个拦截器决定阻止请求继续前进,它可以终止请求流程。
- 如果请求通过了所有的拦截器,则会被发送到最终的目标。3. **响应处理**:
- 当响应从目标返回时,它也会经过相同的拦截器链。
- 每个拦截器可以对响应进行一些后处理,例如修改响应体、添加额外的响应头等。
- 响应最终被传递给最初发起请求的代码。### 应用场景示例
#### AngularJS / Angular
1. **注册拦截器**:
- 在AngularJS中,你可以通过 `$httpProvider.interceptors` 注册HTTP拦截器。
- 在Angular中,你可以使用 `HttpClientInterceptor` 类型来创建自定义拦截器。2. **请求与响应处理**:
- 当发出一个HTTP请求时,请求会经过所有的请求拦截器。
- 当收到响应时,响应会经过所有的响应拦截器。#### Axios
1. **注册拦截器**:
- Axios 提供了一个简单的API来注册请求和响应拦截器。
- 可以通过 `axios.interceptors.request.use` 和 `axios.interceptors.response.use` 来注册拦截器函数。2. **请求与响应处理**:
- 当请求被发送之前,会依次调用请求拦截器。
- 当响应被接收到之后,会依次调用响应拦截器。#### Spring Framework
1. **注册拦截器**:
- 在Spring MVC中,你可以创建实现了 `HandlerInterceptor` 接口的类,并在配置文件中注册它们。
- 这些拦截器可以用来处理请求前后的操作。2. **请求与响应处理**:
- 当一个请求到达控制器之前,会调用拦截器的 `preHandle` 方法。
- 如果请求被允许继续,那么在控制器处理完请求后,会调用拦截器的 `postHandle` 方法。
- 最后,在视图渲染完成后,会调用拦截器的 `afterCompletion` 方法。### 总结
拦截器提供了一种灵活的方式来扩展应用程序的功能,而不需要直接修改核心逻辑。它们可以在不改变现有代码结构的情况下增加新的功能,例如日志记录、性能监控、认证和授权、错误处理等。拦截器的设计使得系统的模块化程度更高,更易于维护和扩展。
源自黑马程序员springboot讲解 讲的很清楚
具体实现:
- 继承Interceptor接口,
- 获取到请求头中的token,
- 得到token就可以让其继续执行,否则返回false:不允许进行
package com.example.day724test.intercepor;
import com.example.day724test.Utils.JwtUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取请求头中的token
String token = request.getHeader("Authorization");
// 判断token是否为空,如果为空表示未登录,返回登录页面
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
/*
放行
*/
return true;
}catch (Exception e){
return false;
/*
不放行
*/
}
}
}
此外,定义
WebConfig类
选择性过滤掉login,register 这两种接口外的其它接口需要实现登录验证
package com.example.day724test.config;
import com.example.day724test.intercepor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//登录接口和注册接口不拦截
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}
修改原来的Controller逻辑:
@GetMapping("/all")
public Result<String> getAllUser(@RequestHeader("Authorization") String token, HttpServletRequest response){ //请求头中获取token
// try {
// Map<String, Object> claims = JwtUtil.parseToken(token);
// return Result.success("所有的文章数据");
// }catch (Exception e){
// return Result.error("未登录");
// }
return Result.success("所有的文章数据,,,,,,,,,");
}
来进行测试:
直接访问/user/all:
携带token之后:
请求成功:基本的登录验证已经完成
项目保存的gitee地址:gitee地址