畅购第二天-网关

畅购-第二天 FastDF文件上传、网关鉴权

当天GIT地址:第二天的GIT地址

一、跨域

出现跨域的原因:

浏览器的同源策略。在统一个协议、ip、 端口下就叫同源。不同的话就会出现跨域问题。

解决方案:

1、在controller加上一个@CrossOrigin就可以就可以允许跨域问题

2、在网关里配置

二、 通用mapper的自定义的方法

多表查询通用mapper无法帮你生成,还是需要自己实现接口的方法

  • 1、根据分类的名称查询品牌信息

    //根据分类的名称查询 品牌的信息
    @Select("select name,image from tb_brand where id in (select  brand_id  from tb_category_brand where category_id  in (select id from tb_category where name=#{categoryName}))")
    List<Map> findBrandListByCategoryName(@Param("categoryName") String categoryName);
    
  • 2、根据分类的名称查询规格

    //根据分类名称查询规格
    @Select("select name,options from tb_spec where template_id in (select template_id from tb_category where name=#{categoryName})")
    List<Map> findSpecListByCategoryName(String categoryName);
    
  • 3、根据分类的名称查询参数

    //根据分类名称查询出场参数
    @Select("select name,options from tb_para where template_id in (select template_id from tb_category where name=#{name})")
    List<Map> findParaListByCategoryName(String name);
    

三、FastDFS文件上传

上传流程:
  • 1、客户端上传一张图片通过Tracker,Tracker再告诉你上传到Storage 位置。

  • 2、并返回你上传的图片在Storage 位置:组名,虚拟磁盘路径,数据两级目录,文件名。

  • 3、你拿着这台服务的协议、端口、IP拼接上这个地址就会获取到图片具体位置

再changgou-service下创建一个文件上传的单独的模块changgou-service-file

导入依赖:
<dependencies>
    <!--springboot web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--fastdfs-->
    <dependency>
        <groupId>net.oschina.zcx7878</groupId>
        <artifactId>fastdfs-client-java</artifactId>
        <version>1.27.0.0</version>
    </dependency>

    <dependency>
        <groupId>com.changgou</groupId>
        <artifactId>changgou_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    
</dependencies>
导入文件上传的工具类:
  • 1、封装上传属性的实体类工具类
  • 2、上传的实体类
大致流程:
  • 1、获取文件进行非空判断

  • 2、获取文件的全名称

  • 3、 获取文件的后缀名 例如 jpg

  • 4、 获取文件的字节数组

  • 5、创建文件上传的封装类,把全名称、后缀名、字节数组传入去

  • 6、进行文件上传。获取到返回值一个数组。

  • 7、获取数值的一个元素是组名、第二个元素是虚拟盘符等等组成的

  • 8、将Storage的地址(就是虚拟机的ip地址和端口)拼接上组名和虚拟盘符等等

    try {
        //上传文件不能为空
        if (file == null) {
            throw new RuntimeException("文件不能为空");
         }
    
    
        //1、获取文件的完整名称
        String originalFilename = file.getOriginalFilename();
    
        //上传文件名称不能为空
        if (StringUtils.isEmpty(originalFilename)) {
            throw new RuntimeException("文件不能为空");
        }
    
        //2、截取文件的后缀名称  例如.jpg  但是不要点 只要jpg
        String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
    
    
        //3、获取文件内容
        byte[] filenameBytes = file.getBytes();
    
    
        //4、创建文件上传的实体类 全名称 内容 后缀名称
        FastDFSFile fastDFSFile = new FastDFSFile(originalFilename,filenameBytes,extName);
    
    
        //5、文件上传
        String[] upload = FastDFSClient.upload(fastDFSFile);
    
    
        //6、返回分装结果  fastDFS访问路径 加上组名
        String url = FastDFSClient.getTrackerUrl() + upload[0] + "/" + upload[1];
    
        return new Result(true, StatusCode.ERROR, "文件上传成功", url);
    
    } catch (Exception e) {
        e.printStackTrace();
    
        return new Result(false, StatusCode.ERROR, "文件上传失败");
    }
    

四、 微服务网关Gateway

优点如下:

  • 安全 ,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
  • 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
  • 易于统一认证授权。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
  • 减少了客户端与各个微服务之间的交互次数

总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控的相关功能。

网关的搭建

1)在changgou_gateway工程中,创建changgou_gateway_system工程

  • 1、坐标的添加

  •  <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
  • yml的配置:

spring:
  application:
    name: sysgateway

    #redis 配置 配合限流使用的
  redis:
    host: 192.168.200.128

  cloud:

    #网关的配置
    gateway:

      #配置了跨域请求
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE


      routes:
        - id: goods
          uri: lb://goods
          predicates:
            - Path=/goods/**
          filters:
            - StripPrefix= 1  #过滤掉斜线后面第一个
            - name: RequestRateLimiter #请求数限流 名字不能随便写
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
                redis-rate-limiter.burstCapacity: 1 #令牌桶总容量



        - id: system
          uri: lb://system
          predicates:
            - Path=/system/**
          filters:
            - StripPrefix= 1  #过滤掉斜线后面第一个




# 将自己的服务注册到eureka上
server:
  port: 9101
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka
  instance:
    prefer-ip-address: true  #将自己的ip注册到eureka上
在网关里配置跨域设置

在这里插入图片描述

过滤器的使用:

注意:

  • 1、类上要加component注解
  • 2、实现GlobalFilter、Ordered 两个接口
@Component
public class IpFilter implements GlobalFilter, Ordered {

    /**
     * 过滤器业务
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取request对象
        ServerHttpRequest request = exchange.getRequest();

        //获取ip
        System.out.println("IP的过滤器执行了");
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        System.out.println("IP的地址是"+remoteAddress.getHostName());


        //放行
        return chain.filter(exchange);
    }


    /**
     * 过滤器执行的顺序
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
}
网关限流

令牌桶的漏桶算法:

令牌桶算法是比较常见的限流算法之一,大概描述如下: 1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理; 2)根据限流大小,设置按照一定的速率往桶里添加令牌; 3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝; 4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除; 5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流

大致概况:

例如这个令牌桶里有200个,突然间来了300请求。有200的请求拿到了令牌去访问了服务。剩下的请求没有拿到,就被丢弃。

在这里插入图片描述

代码的实现漏桶

坐标的引入

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

在启动类里配置KeyResolver bean对象

/**
 * 定义一个KeyResolver
 * @return
 */
@Bean
public KeyResolver ipKeyResolver() {

    return new KeyResolver() {
        @Override
        public Mono<String> resolve(ServerWebExchange exchange) {

            //获取请求的ip地址并对齐执行限流服务
            return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
        }
    };
}

在yml里的配置

在这里插入图片描述

  • burstCapacity:令牌桶总容量。
  • replenishRate:令牌桶每秒填充平均速率。
  • key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

五、BCrypt密码加密

 //原始方法。通过每次生成的盐不用了来达到每次的密文不同
for(int i=0;i<10;i++) {
    //获取盐
    String gensalt = BCrypt.gensalt();
    System.out.println("盐:" + gensalt);
    //基于当前的盐对密码进行加密
    String saltPassword = BCrypt.hashpw("123456", gensalt);
    System.out.println("加密后的密文:" + saltPassword);

    //解密
    boolean checkpw = BCrypt.checkpw("123456", saltPassword);
    System.out.println("密码校验结果:" + checkpw);
}


 System.out.println("===================================================");

      //通过security框架的加密方式 封装了BCrypt
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("123456");
        System.out.println(encode);
        
        //解密看看
        System.out.println(passwordEncoder.matches( "123456",encode));

新增会员时密码加密

修改changgou_service_system项目的AdminServiceImpl

/**
 * 增加
 * @param admin
 */
@Override
public void add(Admin admin){

    //通过security对密码进行添加
    admin.setPassword(new BCryptPasswordEncoder().encode(admin.getPassword()));
    adminMapper.insert(admin);
    
}

登录时判断

  • 1、创建一个新的admin对象。

  • 2、将传入的用户的名称和状态封装到这个新的admin对象。

  • 3、拿着新的admin对象去数据库中,查询是否有这个对象信息

  • 4、有的话拿着从数据库中查询上来的密文密码。和这次传入的密码进行校验。校验成功登录成功

    service层的代码处理

//将传入的用户的名称和状态封装到这个新的admin对象
Admin admin1 = new Admin();
admin1.setLoginName(admin.getLoginName());
admin1.setStatus("1");

//根据用户查询密码
Admin resultAdmin = adminMapper.selectOne(admin1);

//判断是否有这个用户
if (resultAdmin == null) {
    return false;
}

//判断密码不能为空
if (resultAdmin.getPassword().equals("") || resultAdmin.getPassword() == null) {
    return false;
}


//判断密码是否正确 通过BCrypt来进行解密。
if (new BCryptPasswordEncoder().matches(admin.getPassword(), resultAdmin.getPassword())) {
    return true;
}

return false;

六、JWT微服务的鉴权

测试token

坐标:

<dependencies>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>
</dependencies>

具体代码

public static void main(String[] args) {


    Date systemDate = new Date();

    JwtBuilder jwtBuilder = Jwts.builder()
            .setId("66") //设置jwt编码
            .setSubject("卢本伟牛批")//设置主题
            .setIssuedAt(new Date())//设置签发日期
            //.setExpiration(systemDate)  //设置过期时间
            .claim("name","小江")
            .signWith(SignatureAlgorithm.HS256, "lbwq");//设置加密方式为HS256和 密钥 设置位数必须为3位或3位以上


    //生成令牌
    String jwtToken = jwtBuilder.compact();
    System.out.println(jwtToken);



    //解析jwt令牌
    Claims lbwq = Jwts.parser().setSigningKey("lbwq").parseClaimsJws(jwtToken).getBody();
    System.out.println(lbwq);

	//解析得到{jti=66, sub=卢本伟牛批, iat=1601004574, name=小江}
}
鉴权的大致流程

在这里插入图片描述

1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN 
4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误
项目中的具体使用
1.1 令牌的生成
  • 如果service的登录判断成功,就生成一个令牌,令牌携带这用户的名称返回给前端
    /**
     * 登录判断
     * @param admin
     * @return
     */
    @PostMapping("login")
    public Result login(Admin admin) {
        boolean login = adminService.login(admin);
        if (login) {
            //给与令牌
            Map<String,String> info = new HashMap<>();
            info.put("username", admin.getLoginName());

            //生成一个令牌
            String token = JwtUtil.createJWT(UUID.randomUUID().toString(), admin.getLoginName(), null);
            info.put("token", token);
				
            //将令牌返回给前端
            return new Result(true, StatusCode.OK, "登录成功",info);
        } else {
            return new Result(false, StatusCode.ERROR, "登录失败");
        }
    }

}
1.2 网关对令牌的解析
  • 1、获取请求、响应对象
  • 2、根据亲求的url进行判断是不是登录,登录就放行
  • 3、 获取请求头
  • 4、从亲求头获取令牌,如果没有令牌,就返回告诉用户无权访问
  • 5、 如果有令牌。就对其解析。解析成功放行,失败就返回告诉用户无权访问
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取请求
        ServerHttpRequest request = exchange.getRequest();

        //获取响应
        ServerHttpResponse response = exchange.getResponse();

        //如果是登录就放行
        if (request.getURI().getPath().contains("/admin/login")) {
           return chain.filter(exchange);
        }

        //获取请求头
        HttpHeaders headers = request.getHeaders();


        //从请求头里获取令牌信息
        String token = headers.getFirst("token");

        //如果令牌为空,告诉浏览器无权访问
        if (StringUtils.isEmpty(token)) {
            //如果不存在,则向客户端返回错误提示信息
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }


        //6.1 如果令牌存在,解析jwt令牌,判断该令牌是否合法,如果令牌不合法,则向客户端返回错误提示信息
        try {
            //成功放行
            JwtUtil.parseJWT(token);
        } catch (Exception e) {

            e.printStackTrace();

            //如果解析失败,则向客户端返回错误提示信息
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();


        }

        //令牌认证通过放行
        return chain.filter(exchange);
    }

    //拦截的顺序
    @Override
    public int getOrder() {
        return 0;
    }
}
e(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }


        //6.1 如果令牌存在,解析jwt令牌,判断该令牌是否合法,如果令牌不合法,则向客户端返回错误提示信息
        try {
            //成功放行
            JwtUtil.parseJWT(token);
        } catch (Exception e) {

            e.printStackTrace();

            //如果解析失败,则向客户端返回错误提示信息
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();


        }

        //令牌认证通过放行
        return chain.filter(exchange);
    }

    //拦截的顺序
    @Override
    public int getOrder() {
        return 0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值