zuul通过zuulFilter实现token的权限认证和增加公共参数

整理思路:

1、用户登录或者注册后返回token给前台,并同时以userId为key,token为value存入redis

2、通过实现zuulFilter接口拦截所有通过的路由接口(单独放行登录和注册),获取请求的cookie或者请求头有没有的userId和token值跟redis预存的键值对比,相同则放行,不同则鉴权失败

主要代码实现如下:

filterType(): Filter 的类型,前置过滤器返回 PRE_TYPE

filterOrder(): Filter 的顺序,值越小越先执行。这里的写法是 PRE_DECORATION_FILTER_ORDER - 1, 也是官方建议的写法。

shouldFilter(): 是否应该过滤。返回 true 表示过滤,false 不过滤。可以在这个方法里判断哪些接口不需要过滤,本例排除了注册和登录接口,除了这两个接口,其他的都需要过滤。

run(): 过滤器的具体逻辑

1、登录测试类

@Api("登录与注册")
@RequestMapping("/user")
@RestController
public class UserController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/login")
    public ApiMessage login(){

        stringRedisTemplate.opsForValue().set("22","bb");

        return ApiMessage.success("ok","bb");
    }

}

 

2、zuul项目的过滤类

package com.njwd.zuul.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import com.njwd.zuul.constant.ErrorCode;
import com.njwd.zuul.entity.ApiMessage;
import com.njwd.zuul.util.CookiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

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_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 权限验证 Filter
 * 注册和登录接口不过滤
 *
 * 验证权限需要前端在 Cookie 或 Header 中(二选一即可)设置用户的 userId 和 token
 * 因为 token 是存在 Redis 中的,Redis 的键由 userId 构成,值是 token
 * 在两个地方都没有找打 userId 或 token其中之一,就会返回 400 无权限,并给与文字提示
 */
@Component
public class AuthFilter extends ZuulFilter {

    private Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    //排除过滤的 uri 地址
    private static final String LOGIN_URI = "/common/user/login";
    private static final String REGISTER_URI = "/common/user/register";

    //无权限时的提示语
    private static final String INVALID_TOKEN = "invalid token";
    private static final String INVALID_USERID = "invalid userId";

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        logger.info("===uri===", request.getRequestURI());
        //注册和登录接口不拦截,其他接口都要拦截校验 token
        if (LOGIN_URI.equals(request.getRequestURI()) ||
                REGISTER_URI.equals(request.getRequestURI())) {
            return false;
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        //先从 cookie 中取 token,cookie 中取失败再从 header 中取,两重校验
        //通过工具类从 Cookie 中取出 token
        String token = CookiesUtil.getCookies(request, "token");
        System.out.println(token+"===========cookie===================");

        //不验证token时注调该代码
        if (token == null || StringUtils.isEmpty(token)) {
            readTokenFromHeader(requestContext, request);
        } else {
            verifyToken(requestContext, request, token);
        }

        return null;
    }

    /**
     * 从 header 中读取 token 并校验
     */
    private void readTokenFromHeader(RequestContext requestContext, HttpServletRequest request) {
        //从 header 中读取
        String headerToken = request.getHeader("token");
        if (StringUtils.isEmpty(headerToken)) {
            setUnauthorizedResponse(requestContext, INVALID_TOKEN);
        } else {
            verifyToken(requestContext, request, headerToken);
        }
    }

    /**
     * 从Redis中校验token
     */
    private void verifyToken(RequestContext requestContext, HttpServletRequest request, String token) {
        //需要从cookie或header 中取出 userId 来校验 token 的有效性,因为每个用户对应一个token,在Redis中是以 TOKEN_userId 为键的
        String userIdCookie = CookiesUtil.getCookies(request, "userId");
        String rootEnterpriseId = CookiesUtil.getCookies(request, "rootEnterpriseId");
        String type = request.getContentType();
        if (userIdCookie == null || StringUtils.isEmpty(userIdCookie)) {
            //从header中取userId
            String userId = request.getHeader("userId");
            rootEnterpriseId = request.getHeader("rootEnterpriseId");
            if (StringUtils.isEmpty(userId)) {
                setUnauthorizedResponse(requestContext, INVALID_USERID);
            } else {
                String redisToken = stringRedisTemplate.opsForValue().get(userId);
                if (StringUtils.isEmpty(redisToken) || !redisToken.equals(token)) {
                    setUnauthorizedResponse(requestContext, INVALID_TOKEN);
                }else{

                    //针对post请求
                    if (type != null && type.startsWith("application/json")){
                        // 在json参数中添加 userId
                        try {
                            InputStream in = requestContext.getRequest().getInputStream();
                            String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
                            System.out.println("body:" + body);
                            JSONObject json = JSONObject.parseObject(body);
                            json.put("userId", userIdCookie);
                            json.put("rootEnterpriseId", rootEnterpriseId);
                            String newBody = json.toString();
                            System.out.println("newBody:" + newBody);
                            final byte[] reqBodyBytes = newBody.getBytes();
                            requestContext.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;
                                }
                            });
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }else{  //针对get请求

                        //将转换后的数据放入请求参数中
                        Map<String, List<String>> requestQueryParams = requestContext.getRequestQueryParams();
                        if (requestQueryParams==null) requestQueryParams=new HashMap<>();

                        //将要新增的参数添加进去,被调用的微服务可以直接 去取,就想普通的一样,框架会直接注入进去
                        ArrayList<String> paramsList = new ArrayList<>();
                        paramsList.add(rootEnterpriseId);
                        ArrayList<String> paramsList1 = new ArrayList<>();
                        paramsList1.add(userIdCookie);
                        requestQueryParams.put("rootEnterpriseId", paramsList);
                        requestQueryParams.put("userId", paramsList1);
                        requestContext.setRequestQueryParams(requestQueryParams);
                    }
                }
            }
        } else {
            String redisToken = stringRedisTemplate.opsForValue().get(userIdCookie);
            if (StringUtils.isEmpty(redisToken) || !redisToken.equals(token)) {
                setUnauthorizedResponse(requestContext, INVALID_TOKEN);
            }else{

                //针对post请求
                if (type != null && type.startsWith("application/json")){
                    // 在json参数中添加 userId
                    try {
                        InputStream in = requestContext.getRequest().getInputStream();
                        String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
                        System.out.println("body:" + body);
                        JSONObject json = JSONObject.parseObject(body);
                        json.put("userId", userIdCookie);
                        json.put("rootEnterpriseId", rootEnterpriseId);
                        String newBody = json.toString();
                        System.out.println("newBody:" + newBody);
                        final byte[] reqBodyBytes = newBody.getBytes();
                        requestContext.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;
                            }
                        });
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }else{  //针对get请求

                    //将转换后的数据放入请求参数中
                    Map<String, List<String>> requestQueryParams = requestContext.getRequestQueryParams();
                    if (requestQueryParams==null) requestQueryParams=new HashMap<>();

                    //将要新增的参数添加进去,被调用的微服务可以直接 去取,就想普通的一样,框架会直接注入进去
                    ArrayList<String> paramsList = new ArrayList<>();
                    paramsList.add(rootEnterpriseId);
                    ArrayList<String> paramsList1 = new ArrayList<>();
                    paramsList1.add(userIdCookie);
                    requestQueryParams.put("rootEnterpriseId", paramsList);
                    requestQueryParams.put("userId", paramsList1);
                    requestContext.setRequestQueryParams(requestQueryParams);
                }



            }

        }
    }


    /**
     * 设置 400 无权限状态
     */
    private void setUnauthorizedResponse(RequestContext requestContext, String msg) {
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        String result = JSON.toJSONString(ApiMessage.error(ErrorCode.SIGN_ERROR_MESSAGE));
        requestContext.setResponseBody(result);
    }
}

 

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值