一.用户操作权限校验
1.环境准备
1.1 首先是开发环境的准备:
导入redis的jar包
<!--redis存储-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1.2 yml中zuul的配置
server:
port: 8010
spring:
application:
name: zuul-center #给服务起名
profiles:
active: innerTest
servlet:
multipart:
max-request-size: 1000MB
max-file-size: 1000MB
eureka:
client:
serviceUrl:
defaultZone : http://eurekaServer1.com:8000/eureka,http://eurekaServer2.com:8001/eureka
registry-fetch-interval-seconds: 5
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 # 熔断超时时长:6000ms
threadpool:
default:
coreSize: 500
maximumSize: 1000
allowMaximumSizeToDivergeFromCoreSize: true
maxQueueSize: -1
feign:
hystrix:
enable: true
ribbon:
ConnectTimeout: 2000 # ribbon链接超时时长
ReadTimeout: 2000 # ribbon读取超时时长
MaxAutoRetries: 0 # 当前服务重试次数
MaxAutoRetriesNextServer: 1 # 切换服务重试次数
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试,只对get请求重试
management:
server:
add-application-context-header: true
endpoints:
web:
exposure:
include: '*'
#进行token校验的url地址,全部小写
authfilter:
ignoreUri: /user/sysuser/login,/user/sysuser/sendcode ,/user/sysuser/checkoutcode, /user/sysuser/getverify,/user/sysuser/checkverify
zuul:
prefix: /api #给网关加上前缀
sensitive-headers: Access-Control-Allow-Origin
routes:
服务名1: /user/** #服务1映射到/user/**
服务名2: /zhzd/** #服务2
服务名3: /support/** #服务3的前缀
ribbonIsolationStrategy: THREAD
host:
maxTotalConnections: 1000
maxPerRouteConnections: 100
socket-timeout-millis: 120000
connect-timeout-millis: 120000
---
spring:
profiles: innerTest
redis:
database: 0
host: 192.168.1.199
port: 6379
password: XXX
block-when-exhausted: true
jedis:
pool:
max-idle: 200
max-active: 200
max-wait: -1
min-idle: 0
timeout: 10000
2.下面是具体代码
编写一个类AuthFileter继承ZuulFilter
package com.zhzd.zuulcenter.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.faw.jetta.diagnosisCommon.constant.RedisConstants;
import com.faw.jetta.diagnosisCommon.enums.ExceptionEnum;
import com.faw.jetta.diagnosisCommon.exception.ProjectException;
import com.faw.jetta.diagnosisCommon.http.HttpResult;
import com.faw.jetta.diagnosisCommon.utils.CookieUtils;
import com.faw.jetta.diagnosisCommon.utils.JsonUtils;
import com.faw.jetta.diagnosisCommon.utils.ResultUtils;
import com.faw.jetta.diagnosisCommon.utils.StringUtil;
import com.faw.jetta.pojo.diagUserPojo.DTO.SysMenuDTO;
import com.faw.jetta.pojo.diagUserPojo.DTO.SysRoleDTO;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* 用户登录Token拦截器
*/
@Slf4j
@Component
public class AuthFilter extends ZuulFilter {
@Value("${authfilter.ignoreUri}")
private List<String> ignoreUri;
@Autowired
StringRedisTemplate stringRedisTemplate;
//无权限时的提示语
private static final String INVALID_TOKEN = "登录信息无效,请重新登录";
private static final String INVALID_USERID = "用户信息无效,请重新登录";
private static final String USERINFOREDISKEY = "user:login:dto:";
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String uri = request.getRequestURI().toLowerCase();
//log.info("uri="+uri);
//判断是否需要拦截
if (request.getHeader("content-type") != null && request.getHeader("content-type").indexOf("multipart/form-data") > -1) {
return false;
}
if (ignoreUri.contains(uri)) {
return false;
} else {
if (request.getMethod().equals("OPTIONS")) {
return false;
}
return true;
}
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
try {
//先从 cookie 中取 token,cookie 中取失败再从 header 中取,两重校验
//通过工具类从 Cookie 中取出 token
String token;
String tokenCookie = CookieUtils.getCookieValue(request, "x-auth-token");
String tokenHeader = request.getHeader("x-auth-token");
token = tokenHeader;
if (token == null || StringUtils.isEmpty(token)) {
token = tokenCookie;
}
verifyToken(requestContext, request, token);
//从redis中获取用户登陆信息, 用来校验用户的访问权限
//1.获取到访问路径
String requestURI = request.getRequestURI();
//2.获取到用户登陆信息
String userId = stringRedisTemplate.opsForValue().get(RedisConstants.loginUid + token);
String userDTOJson = stringRedisTemplate.opsForValue().get(RedisConstants.loginUserDTO + userId);
JSONObject jsonObject = JSONObject.parseObject(userDTOJson);
JSONArray roleDTOListJson = jsonObject.getJSONArray("roleList");
List<SysRoleDTO> roleDTOList = JsonUtils.jsonToList(roleDTOListJson.toJSONString(), SysRoleDTO.class);
List<SysMenuDTO> menuDTOList = new ArrayList<>();
//遍历角色集合, 给角色赋值
for (SysRoleDTO sysRoleDTO : roleDTOList) {
menuDTOList.addAll(sysRoleDTO.getSysMenuDTOList());
}
//校验是否具有该访问权限
for (SysMenuDTO sysMenuDTO : menuDTOList) {
String menuUrl = sysMenuDTO.getMenuUrl();
if (StringUtils.isNotEmpty(requestURI)&&StringUtils.isNotEmpty(menuUrl)&&requestURI.equals(menuUrl)) {
return null;
}
}
//TODO 没有权限访问时, 应该返回一个提示
ResultUtils resultUtils = new ResultUtils<>();
resultUtils.setCode(401);
resultUtils.setMsg("没有权限访问");
String responseJson = JsonUtils.objectToJson(resultUtils);
requestContext.setResponseBody(responseJson);
} catch (Exception e) {
e.printStackTrace();
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
log.error("非法访问,未授权,地址:{}", request.getRemoteHost(), e );
}
return null;
}
/**
* 从Redis中校验token
*/
private void verifyToken(RequestContext requestContext, HttpServletRequest request, String token) {
//需要从cookie或header 中取出 userId 来校验 token 的有效性,因为每个用户对应一个token,在Redis中是以 TOKEN_userId 为键的
String userIdFromCookie = CookieUtils.getCookieValue(request, "uid");
String userIdFromHeader = request.getHeader("uid");
//禁止前端直接从参数中传递uid参数,防止进行跨用户攻击
/**
String uid = request.getParameter("uid") ;//从参数中获取uid
if(uid == null || StringUtils.isEmpty(uid))
{
uid = userIdFromHeader;
}
if(uid == null || StringUtils.isEmpty(uid))
{
uid = userIdFromCookie;
}
**/
String uid = null;
//如果传递了token,则要校验token的有效性
if (token != null && !StringUtils.isEmpty(token)) {
String redisUid = stringRedisTemplate.opsForValue().get(RedisConstants.loginUid + token);
if (uid == null || StringUtils.isEmpty(uid)) { //客户端没有传递uid
uid = redisUid;// 系统自动将token转成uid
if (uid != null && !StringUtils.isEmpty(uid)) {
addUidParam(uid, requestContext, token);
} else { //客户端传递了一个无效的token
setUnauthorizedResponse(requestContext, INVALID_TOKEN);
}
} else { //有token,有uid,验证二者是否一致
if (redisUid.equalsIgnoreCase(uid)) {
addUidParam(uid, requestContext, token);
} else { //传递的token 和uid不一致
setUnauthorizedResponse(requestContext, INVALID_USERID);
}
}
} else { //没有传递token,放过,微服务进行验权
addUidParam("0", requestContext, token);
}
}
/**
* 向请求信息中增加uid参数
*
* @param uid 用户uid值
* @param ctx
*/
private void addUidParam(String uid, RequestContext ctx, String sessionIdInHeader) {
try {
if (sessionIdInHeader != null && !StringUtils.isEmpty(sessionIdInHeader)) {
ctx.addZuulRequestHeader("x-auth-token", sessionIdInHeader);
}
//获取Redis中的用户信息
//TODO 现在dealerId和comId指的是一个id,提供给外部调用的是comId(整型id)
String dealerId = "0";
String comId = "0";
String comIdStr = "0";
String userType = "0";
//整型userId用作门店请求头
String userIdInt = "0";
try {
String userStr = stringRedisTemplate.opsForValue().get(USERINFOREDISKEY + uid);
if (!StringUtils.isEmpty(userStr)) {
JSONObject object = JSONObject.parseObject(userStr);
dealerId = object.getString("dealerId");
comId = object.getString("companyIdInt");
comIdStr = object.getString("companyId");
userType = object.getString("userType");
userIdInt = object.getString("idInt");
}
} catch (Exception e) {
e.printStackTrace();
}
HttpServletRequest request = ctx.getRequest();
String method = request.getMethod();
log.info(String.format("%s >>> %s", method, request.getRequestURL().toString()));
InputStream in = request.getInputStream();
String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
// 如果body为空初始化为空json
if (StringUtils.isBlank(body)) {
body = "{}";
}
//log.info("body" + body);
// 转化成json
//JSONObject json = JSONObject.parseObject(body);
if ("GET".equalsIgnoreCase(method)) {
request.getParameterMap();
Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
if (requestQueryParams == null) {
requestQueryParams = new HashMap<>();
}
//1.将字符串型用户id放入head中
List<String> uids = new ArrayList<>();
uids.add(uid);
requestQueryParams.put("uid", uids);
//2.将整型用户id放入head中
List<String> userIdInts = new ArrayList<>();
userIdInts.add(userIdInt);
requestQueryParams.put("userIdInt", userIdInts);
//3.将dealerId(整型)放入head中
List<String> dealerIds = new ArrayList<>();
dealerIds.add(dealerId);
requestQueryParams.put("dealerId", dealerIds);
//4.将comId(整型)放入head中
List<String> comIds = new ArrayList<>();
comIds.add(comId);
requestQueryParams.put("comId", comIds);
//4.1将comIdStr(字符串类型放入head中)
List<String> comIdStrList = new ArrayList<>();
comIdStrList.add(comIdStr);
requestQueryParams.put("comIdStr", comIdStrList);
//5.将用户类型放入head中
List<String> userTypes = new ArrayList<>();
userTypes.add(userType);
requestQueryParams.put("userType", userTypes);
ctx.setRequestQueryParams(requestQueryParams);
} else if ("POST".equalsIgnoreCase(method)) {
//json.put("uid",uid);
// byte[] reqBodyBytes = JSON.toJSONString(json).getBytes();
byte[] reqBodyBytes = body.getBytes("UTF-8");
Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
if (requestQueryParams == null) {
requestQueryParams = new HashMap<>();
}
List<String> uids = new ArrayList<>();
uids.add(uid);
requestQueryParams.put("uid", uids);
List<String> dealerIds = new ArrayList<>();
dealerIds.add(dealerId);
requestQueryParams.put("dealerId", dealerIds);
List<String> comIds = new ArrayList<>();
comIds.add(comId);
requestQueryParams.put("comId", comIds);
List<String> userTypes = new ArrayList<>();
userTypes.add(userType);
requestQueryParams.put("userType", userTypes);
ctx.setRequestQueryParams(requestQueryParams);
ctx.setRequest(new HttpServletRequestWrapper(request) {
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(reqBodyBytes);
}
@Override
public int getContentLength() {
return reqBodyBytes.length;
}
@Override
public long getContentLengthLong() {
return reqBodyBytes.length;
}
});
}
// ctx.setSendZuulResponse(false);
} catch (Exception e) {
log.error("addUidParam", e);
}
}
/**
* 设置 401 无权限状态
*/
private void setUnauthorizedResponse(RequestContext requestContext, String msg) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.OK.value());
HttpResult vo = HttpResult.error();
vo.setCode(401);
vo.setMsg(msg);
String result = JSON.toJSONString(vo);
requestContext.setResponseBody(result);
}
}