微服务的登录校验(gateway过滤器or拦截器实现)
问题来源在做找房微服务的时候,一般只有登录的用户能够预约房源操作,或者修改资料等密码操作,管理员也需要登录的情况下才能对房源进行管理上架、添加等操作。假如我们没一个方法都分别对其权限鉴定,用户判断是否登录,那么一个团队的开发每一个成员涉及到需要鉴权或者判断用户是否登录的情况,都需要写一套相同的代码,或者调用相同的接口,可是这些实际上应该是与本次逻辑不相干的代码,开发小组的成员应该更专注于本次需..
问题来源
在做找房微服务的时候,一般只有登录的用户能够预约房源操作,或者修改资料等密码操作,管理员也需要登录的情况下才能对房源进行管理上架、添加等操作。
假如我们没一个方法都分别对其权限鉴定,用户判断是否登录,那么一个团队的开发每一个成员涉及到需要鉴权或者判断用户是否登录的情况,都需要写一套相同的代码,或者调用相同的接口,可是这些实际上应该是与本次逻辑不相干的代码,开发小组的成员应该更专注于本次需要实现的逻辑与功能。
通过接口调用,或者写一套相同的鉴权、判断用户是否登录的方法,极大的干扰开发的进度,也及其不利于后续的迭代开发。例子:如果鉴权、登录判断逻辑或者接口方法发生改变,那么一套系统中很多部分代码都需要修改代码。
理解下文首先要清楚一些最基本的概念
过滤器:
过滤器依赖servlet容器。过滤所有请求。可以在对服务器方法请求前后进行一些操作,能够决定是否放行请求。例如:字串编码,提前在request中设置一些参数等。
拦截器:
拦截器依赖web框架,springMVC中依赖的mvc框架,所以它能够spring依赖注入DI操作。实现上是反射机制实现的。对某个service方法前或后执行某部分逻辑。能够决定请求是否能被放行。功能类似过滤器
执行顺序
图片来源:https://www.cnblogs.com/juanzila/p/11276067.html
解决方法
第一种:过滤器方法实现。
本项目中遵循restful原则,GET请求一般为查询资源,POST,PUT,DELETE为修改添加资源操作。
gateway依赖
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
编写过滤器
@Component
public class MygateWayFilter implements GlobalFilter, Ordered {
@Autowired
JwtUtil jwtUtil;
@Autowired
RedisTemplate redisTemplate;
/**
* 可以用于验证用户登录状态,设置某部分请求信息等。
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String uri = request.getURI().toString();
//判断当前是什么类型请求GET请求直接放行
if (StringUtils.equals(request.getMethod().toString(), "GET")){
return chain.filter(exchange);
}
//不是GET,判断是不是登录
if (uri.contains("login")){
return chain.filter(exchange);
}
//如果不是GET请求,判断是否已经登录,token是否过期
//获取cookie中的token令牌
MultiValueMap<String, HttpCookie> multiValueMap = exchange.getRequest().getCookies();
List<HttpCookie> cookies = multiValueMap.get("token");
if (cookies != null && cookies.size() != 0) {
String token = cookies.get(0).getValue();
//token为空说明未登录,不放行
if (StringUtils.isBlank(token)){
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
//token not null
try {
Claims claims = jwtUtil.parseJWT(token);
String userId = claims.getId();
Object userLoginIP = redisTemplate.opsForValue().get("token_"+userId);
//判断是否被token盗用
if (!StringUtils.equals(String.valueOf(userLoginIP), CusAccessObjectUtil.getIpAddress(request))){
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
return chain.filter(exchange);
}catch (Exception e){
e.printStackTrace();
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
}
//没有cookie不放行
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
@Override
public int getOrder() {
return 0;
}
}
第二种:拦截器方法实现。(推荐单服务、单系统情况)
第一步:编写拦截器类
import com.zxf.service.UserService;
import com.zxf.serviceResult.ServiceResult;
import com.zxf.utils.CookieUtils;
import com.zxf.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登录判断拦截
*/
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
@Autowired
JwtUtil jwtUtil;
@Value("${token_name}")
private String TOKENNAME;
@Value("${redirectURL}")
private String redirectURL;
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = CookieUtils.getCookieValue(request, TOKENNAME);
String requestURL = request.getRequestURI();
System.out.println(requestURL);
//不存在token,未登录的情况下不放行,直接跳转到登录页面,跳转到登录页面以后在跳转回当前页面
if (token == null){
response.sendRedirect(redirectURL+"?url="+requestURL);
return false;
}
//执行判断是否存在盗用,或者过期
ServiceResult serviceResult = userService.checkToken(token, request);
if (serviceResult.getStatus() != ServiceResult.Status.SUCCESS.getCode()){
response.sendRedirect(redirectURL+"?url="+requestURL);
return false;
}
return true;
}
}
第二步:编写拦截器配置类
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
/**
* 配置拦截的请求,与不拦截的请求
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login", "/**/loginPage", "/**/static/**"); //这些请求不拦截
}
}
更多推荐
所有评论(0)