用服务网关Zuul+vue+JWT实现服务对外隐藏和登录拦截

1.背景

Eureka用于服务的注册于发现
Feign支持服务的调用以及均衡负载
Hystrix处理服务的熔断防止故障扩散
后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。
当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。

2.网关名称解释

网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。
网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。

3.Spring Cloud Zuul 简介

Zuul 是Netflix
提供的一个开源组件,致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。也有很多公司使用它来作为网关的重要组成部分。Spring
Cloud 体系收录的该模块,主要用于提供动态路由、监控、安全控制、限流配额等,可以将内部微服务API统一暴露。

4. 微服务下的的网关模式

在spring cloud 体系当中,内部的服务全部进行隐藏,对外只有一个对外暴露的机制,这就是spring cloud zuul
网关。架构图如下所示

在这里插入图片描述

2.简单使用(配置Zuul及测试)

2.1 pom

Zuul

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 加入网关依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

user里测试

<!--JWT-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

2.2 bootstrap.yml

server:
  port: 7000
spring:
  application:
    name: zuul
  cloud:
    config:
      name: me-config
      profile: dev
      discovery:
        enabled: true
        service-id: config
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka

2.3 启动类

@SpringBootApplication
//标注当前是网关
@EnableZuulProxy
@EnableDiscoveryClient

2.4 filter

@Component
public class AuthorZuulFilter extends ZuulFilter{

    //指定当前过滤器的类型
    /*PRE:在请求被路由之前调用,可以使用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试Log等。
     *ROUTE:将请求路由到对应的微服务,用于构建发送给微服务的请求。
     *POST:在请求被路由到对应的微服务以后执行,可用来为Response添加HTTP Header、将微服务的Response发送给客户端*等。
     *ERROR:在其他阶段发生错误时执行该过滤器。
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }


    //指定过滤器的执行顺序
    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    }


    //配置是否启用
    @Override
    public boolean shouldFilter() {
        //全部都拦截
        return true;
    }

    //指定过滤器中的具体业务代码
    @Override
    public Object run() throws ZuulException {
        System.out.println("拦截,无法访问!");
        return null;
    }
}

2.5 访问时,必须从网关的端口开始访问,加上其它服务的名称及方法

如:在这里插入图片描述

2.6 测试(加密,解密)

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestJWT {

    //加密
    @Test
    public void testSecert(){

        long l = System.currentTimeMillis();
            l+=5*60*1000;

        Date date = new Date(l);
        //定义加密的算法 私钥
        Algorithm algorithmHS = Algorithm.HMAC256("secret");
        Map map = new HashMap<>();
        map.put("alg","HS256");
        map.put("typ","jwt");
        //头部部分
        String token = JWT.create().withHeader(map).
                //主体 载荷
                //创建的主题
                        withSubject("this is token").
                //创建者
                        withIssuer("wang").
                //签发的时间
                        withIssuedAt(new Date()).
                        //自定义的信息
                        withClaim("username","张三").
                        withClaim("userpass",2).
                //过期时间
                        withExpiresAt(date).
                //签名
                        sign(algorithmHS);
        System.out.println("==========================="+token);
    }

    @Test
    public void Verify(){
        String token = "eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0aGlzIGlzIHRva2VuIiwidXNlcnBhc3MiOjIsImlzcyI6IndhbmciLCJleHAiOjE2MDMyOTE1MDksImlhdCI6MTYwMzI5MTIwOSwidXNlcm5hbWUiOiLlvKDkuIkifQ.Bvu0uFaPKY8ZotSZUY1yXtks6xSBop7xA2yvN5wdfrA";
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier verfiy = JWT.require(algorithm).withIssuer("wang").build();
        //解析token
        DecodedJWT verify =  verfiy.verify(token);
        //获取你所需要的内容
        Claim username = verify.getClaim("username");
        Claim userpass = verify.getClaim("userpass");

        System.out.println("当前用户名为 ===="+username.asString()+"用户密码为========"+userpass.asInt());
    }
}

3.登录拦截

3.1.配置Zuul

1.pom
		<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 加入网关依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>

        <dependency>
            <groupId>com.wo</groupId>
            <artifactId>pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>
2.bootstrap.yml
server:
  port: 7000
spring:
  application:
    name: zuul
  cloud:
    config:
      name: me-config
      profile: dev
      discovery:
        enabled: true
        service-id: config
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka

3.启动类
@SpringBootApplication
//标注当前是网关
@EnableZuulProxy
@EnableDiscoveryClient
4.filter

@Component
public class AuthorZuulFilter extends ZuulFilter{

    //指定当前过滤器的类型
    /*PRE:在请求被路由之前调用,可以使用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试Log等。
     *ROUTE:将请求路由到对应的微服务,用于构建发送给微服务的请求。
     *POST:在请求被路由到对应的微服务以后执行,可用来为Response添加HTTP Header、将微服务的Response发送给客户端*等。
     *ERROR:在其他阶段发生错误时执行该过滤器。
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }


    //指定过滤器的执行顺序
    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    }


    //配置是否启用
    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String requestURI = request.getRequestURI();
        if ("/user/login".equals(requestURI)){
            return false;
        }
        return true;
    }

    //指定过滤器中的具体业务代码
    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        Cookie[] cookies = request.getCookies();
        String token=null;

        for (Cookie cook:cookies
                ) {
            if (cook.getName().equals("token")){
                token= cook.getValue();
            }
        }
        //定义返回值
        BaseResp baseResp = new BaseResp();
        try {
            Algorithm algorithm = Algorithm.HMAC256("secret");
            JWTVerifier verfiy = JWT.require(algorithm).withIssuer("wang").build();
            //解析token
            DecodedJWT verify =  verfiy.verify(token);
            //获取你所需要的内容
            Claim username = verify.getClaim("username");
            Claim userid = verify.getClaim("userpass");

            if (username==null&&userid==null){
                currentContext.getResponse().setContentType("application/json");

                baseResp.setCode(10002l);
                baseResp.setMessage("token验证失败,被拦截");
                currentContext.setResponseBody(JSONObject.toJSONString(baseResp));
            }

        }catch (Exception ex){
            baseResp.setCode(10003l);
            baseResp.setMessage("token过期,请重新登录");
            currentContext.getResponse().setContentType("application/json");

            currentContext.setResponseBody(JSONObject.toJSONString(baseResp));
        }
        return null;
    }
}

3.2 配置User

1.pom新增
<!--JWT-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
2 bootstrap.yml
server:
  port: 8003
spring:
  application:
    name: user
  cloud:
    config:
      name: me-config
      profile: dev
      discovery:
        enabled: true
        service-id: config
feign:
  hystrix:
    enabled: true #开启降级
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka
#开启配置自动刷新
management:
  endpoints:
    web:
      exposure:
        include: refresh
3.dao
public interface UserRespository extends JpaRepository<TrUser,Integer> {
    TrUser findByLoginNameAndPassword(String loginName, String password);
}
4.service
public interface UserService {
    BaseResp login(TrUser user);
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserRespository userRespository;

    @Override
    public BaseResp login(TrUser user) {

        TrUser byLoginNameAndPassword = userRespository.findByLoginNameAndPassword(user.getLoginName(), user.getPassword());
        BaseResp baseResp  = new BaseResp();
        if (byLoginNameAndPassword!=null){
            //如果user对象不为空,说明用户名,密码输入正确,将用户名,密码放置到jwt生成token
            long l = System.currentTimeMillis();
            l+=60*60*1000;

            Date date = new Date(l);
            //定义加密的算法 私钥
            Algorithm algorithmHS = Algorithm.HMAC256("secret");
            Map map = new HashMap<>();
            map.put("alg","HS256");
            map.put("typ","jwt");
            //头部部分
            String token = JWT.create().withHeader(map).
                    //主体 载荷
                    //创建的主题
                            withSubject("this is token").
                    //创建者
                            withIssuer("wang").
                    //签发的时间
                            withIssuedAt(new Date()).
                    //自定义的信息
                            withClaim("username",byLoginNameAndPassword.getLoginName()).
                            withClaim("userpass",byLoginNameAndPassword.getPassword()).
                    //过期时间
                            withExpiresAt(date).
                    //签名
                            sign(algorithmHS);
            baseResp.setCode(200l);
            baseResp.setMessage(token);
            return baseResp;
        }
        baseResp.setCode(10001l);
        baseResp.setMessage("用户密码或者账户有误");
        return baseResp;
    }
}
5.controller
package com.wo.controller;

import com.wo.client.GoodsClient;
import com.wo.client.ShopCartClient;
import com.wo.pojo.BaseResp;
import com.wo.pojo.Goods;
import com.wo.pojo.TrUser;
import com.wo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RefreshScope//开启配置刷新 用post请求http://localhost:8003/actuator/refresh刷新配置
public class UserController {

    @Autowired
    GoodsClient goodsClient;
    @Autowired
    UserService userService;
    @Autowired
    ShopCartClient shopCartClient;

    //user直接调用goods的findAll方法    user->goods
    @RequestMapping("/user/findAll")
    public List<Goods> findAll(){
        return goodsClient.findAll();
    }

    //shopcart调用goods的findAll方法,user调用shopcart的findAndfind方法    user->shopcart->goods
    @RequestMapping("/user/findAndfind")
    public String findAndfind(){
        return shopCartClient.findAndfind()+"****user调用shopcart的findAndfind方法成功";
    }

    @RequestMapping("/login")
    public BaseResp login(@RequestBody TrUser user){
        return userService.login(user);
    }
}

3.2 前台VUE

1.建好工程,导入依赖

npm install axios
npm install vue-cookie
npm i element-ui -s

2.跨域
proxyTable: {
      '/api': {
        target: 'http://localhost:7000/', // 设置你调用的接口域名和端口号
        changeOrigin: true,   // 跨域
        pathRewrite: {
          '^/api': '/'
        }
      }
    },
3.main.js配置全局
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import cookie from 'vue-cookie'
Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(cookie)
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

4.路由
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import index from '@/components/index'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/index',
      name: 'index',
      component: index
    }
  ]
})
5.登陆页面
<template>
  <div class="hello">
    <el-form ref="form" :model="form" label-width="80px">
      <el-form-item label="用户名">
        <el-input v-model="form.loginName"></el-input>
      </el-form-item>
      <el-form-item label="密码">
        <el-input v-model="form.password"></el-input>
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="onSubmit">立即创建</el-button>
        <el-button>取消</el-button>
      </el-form-item>
    </el-form>

  </div>
</template>

<script>
  import axios from 'axios'
  export default {
    data () {
      return {
        form:{}
      }
    },
    methods:{
      onSubmit:function () {
        axios.post("api/user/login",this.form).then(res=>{
          //登录成功
          if (res.data.code==200){
            var token =  res.data.message;
            //将token设置到cookie中
            this.$cookie.set("token",token)

            this.$router.push("/index")
          }else{
            alert(res.data.message)
          }
        })
      }
    },
  }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  h1, h2 {
    font-weight: normal;
  }
  ul {
    list-style-type: none;
    padding: 0;
  }
  li {
    display: inline-block;
    margin: 0 10px;
  }
  a {
    color: #42b983;
  }
</style>

6.主页面
<template>
    <div></div>
</template>

<script>
  import axios from 'axios'
  //设置让axios 携带cookie去请求后端
  axios.defaults.withCredentials=true
    export default {
        name: "",
      data(){
          return{

          }
      },
      methods:{
        findAll:function () {
          axios.get("api/goods/findAll").then(res=>{
                if (res.data.code==200){
                  alert(res.data.message)
                } else{
                  alert(res.data.message)
                  this.$router.push("/")
                }
          })
        }
      },
      mounted(){
          this.findAll();
      }
    }
</script>
<style scoped>
</style>

7.运行测试

在这里插入图片描述
如果用户名密码正确:进入主页
如果没有登录(token)或token错误,提示错误,然后返回登陆页面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值