个人博客 毕业设计7--Spring Cloud

一、概述

spring cloud其实是一个集合啦,不是什么新的技术,把一堆的技术,像spring boot那样打包好来用。用spring cloud必须得用到spring boot。

主要框架

  • 服务发现——Netflix Eureka
  • 服务调用——Netflix Feign
  • 熔断器——Netflix Hystrix :是一个处理错误的东西。发生某些错误时,如果不及时更改,会发生什么奇怪得事情,熔断器就是直接返回一个错误的数据,虽然错误,但不会导致应用全部崩坏
  • 服务网关——Netflix Zuul :像现在的每一个微服务的端口号都不一样,服务网关就是把这端口号集合成一个,然后用的时候分发出去
  • 分布式配置——Spring Cloud Config :把所有微服务的application.yml放到一个统一的地方。因为Java程序打包不会把配置文件也放进去,启动的时候可以修改配置文件以便后续修改。
  • 消息总线 —— Spring Cloud Bus:类似热部署,修改文件不需要再重新启动一遍

版本

spring boot和spring cloud的版本号一定要对上,官方链接比如我spring boot 2.3.4 ,就要用Hoxton.SR9
在这里插入图片描述

二、Eureka

Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目 spring-cloud-netflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件: Eureka Server和Eureka Client。

人话模式:现在各个微服务模块之间都不认识,都不知道彼此是什么,那么就要注册一个Eureka Server,这个是空白的东西。然后再将各个微服务模块做成一个个Eureka Client,注册在Eureka Server中,这样他们就能彼此之间认识了。

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注 册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点 的信息可以在界面中直观的看到。

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也 就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会 向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有 接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90 秒)。

1、Eureka(微服务模块)服务端开发

1)导包

父类导包,固定版本

    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
  • dependencyManagement是用来控制版本的

新建eureka模块导包

添加server模块,这个当作服务器,其他模块是用户

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

这里的导包,非常神奇呢,一开始是unknown的,然后上网找了几天解决方法都没有,过了几天再刷新一下他就自己OK了。。。

2)修改yml,主类添加注解@EnableEurekaServer

server:
  port: 6868
eureka:
  client:
    register-with-eureka: false #是否将自己注册到Eureka服务中,本身就是服务端无需注册
    fetch-registry: false #是否从Eureka中获取注册信息,服务端不需要获取注册信息
    service-url: #Eureka客户端与Eureka服务端进行交互的地址
      defaultZone: http://127.0.0.1:${
    
    server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaApplication.class, args);
    }
}

http://localhost:6868/
在这里插入图片描述

2、其他模块实现Eureka客户端注册

其他模块全部添加包、yml和主程序类注解@EnableEurekaClient

	  <dependency>
		  <groupId>org.springframework.cloud</groupId>
		  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	  </dependency>
eureka:
  client:
    service-url:
     defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true #false只允许本地使用,true可以在服务器上使用
@SpringBootApplication
@EnableEurekaClient
public class ArticleApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(ArticleApplication.class, args);
	}
	
}

三、Feign实现服务间的调用

可以实现A模块调用B模块的方法,前提是开启Eureka服务和注册Eureka客户端
以QA调用Base模块为例子

1、以QA调用Base模块为例子

1)在主动调用一方导包

QA模块添加

	  <dependency>
		  <groupId>org.springframework.cloud</groupId>
		  <artifactId>spring-cloud-starter-openfeign</artifactId>
	  </dependency>

2)主动调用方启动类添加注解

QaApplication :

@EnableDiscoveryClient
@EnableFeignClients
public class QaApplication {
    
    
//xxx
}

3)主动调用方模块添加接口

QA模块-client包-LabelClient接口

@FeignClient("tensquare-base")
public interface LabelClient {
    
    

    @GetMapping("/label/{labelId}")
    public Result findById(@PathVariable("labelId") String labelId);
}

  • @FeignClient()参数输入调用哪个服务(模块)名字,名字就是yml中的spring: application: name:,切不能包含下划线_
    spring:
      application:
    #    微服务通过application-name来进行连接,不能用_
        name: tensquare-base  #就是这个名字
    
  • 然后接口里面,要什么方法,就写什么方法,这个方法就是base模块中的方法。接口嘛,就写到名字和参数这一步就OK,具体逻辑就是调用模块中的逻辑

3.5)Result类记得添加无参构造器

远古时代忘记加无参构造器了
在实体类添加@NoArgsConstructor

4)主动调用方添加方法

QA模块中的ProblemController
这里的方法是第3步中调用的方法

//localhost:9003/problem/label/{labelId}
	@GetMapping("label/{labelId}")
	public Result findLabelById(@PathVariable("labelId") String labelId){
    
    
		Result result = labelClient.findById(labelId);
		return result;
	}

在这里插入图片描述

四、交友微服务

1、说明

交友数据库表:
在这里插入图片描述

@Entity
@Data
@Table(name = "tb_friend")
@IdClass(Friend.class)
public class Friend implements Serializable {
    
    
    @Id
    private String userid;

    @Id
    private String friendid;

    private String isLike;
}
@Entity
@Data
@Table(name = "tb_nofriend")
@IdClass(NoFriend.class)
public class NoFriend {
    
    
    @Id
    private String userid;

    @Id
    private String friendid;
}
  • 这里需要用到@IdClass,我也不知道为啥要,反正不加就出错。。

用户数据库表:
在这里插入图片描述

1)当A向B点击喜欢时

A的关注数+1,B的粉丝+1。判断,如果B也喜欢A,那么双方isLike都变成1;如果A喜欢B,B不喜欢A,那么A的isLike为0

2)当A对B取消喜欢时

就是取关,A的关注数-1,B的粉丝-1。并且, 对tb_nofriend表添加两个人的ID。

2、添加好友

它是这样的,推荐列表,一个人,显示2个按钮,一个喜欢按钮,一个X按钮,点了喜欢,就在Friend表中添加数据。当点击X,就是不喜欢该人,就在NoFriend表中添加数据,推荐表就不会出现该人了
在这里插入图片描述
controller

@RestController
@RequestMapping("/friend")
public class FriendController {
    
    

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private FriendService friendService;

    @Autowired
    private JwtUtil jwtUtil;
    /*
     *
     * @param friendid 对方ID
     * @param type 1喜欢,2不喜欢
     * @return entity.Result
     */
    @PutMapping(value = "like/{friendid}/{type}")
    public Result addFriend(@PathVariable String friendid,@PathVariable String type){
    
    
        //验证登陆
        String token = (String) httpServletRequest.getAttribute("claims_user");
        if (token==null || "".equals(token)){
    
    
            //如果当前用户没有user角色
            return new Result(false, StatusCode.ERROR,"无权限操作");
        }

        //判断是添加好友好事添加非好友
        if (type!=null) {
    
    //非空
            //添加好友
            if (type.equals("1")) {
    
    
                //将token转义
                Claims claims = jwtUtil.parseJWT(token);
                String userid = claims.getId();
                //根据不同情况添加好友
                int flag = friendService.addFriend(userid, friendid);
                //重复添加好友
                if (flag==0){
    
    
                    return new Result(false, StatusCode.ERROR,"重复添加好友");
                }
                //添加成功
                if (flag == 1) {
    
    
                    return new Result(true, StatusCode.OK,"添加成功");
                }

            }
            //添加非好友
            else if (type.equals("2")){
    
    


            }
            //如果都不是
            else return new Result(false, StatusCode.ERROR,"参数异常");


        }
        //如果type为空
        return new Result(false, StatusCode.ERROR,"参数异常");
    }
}

  • 用到了JWT权限登陆验证
  • addFriend()在service用于返回一个int类型,0就重复添加,1就添加成功

service

@Service
public class FriendService {
    
    

    @Autowired
    private FriendDao friendDao;

    @Transactional
    public int addFriend(String userid,String friendid){
    
    
        //如果已经加为好友了
        if (friendDao.findByUseridAndFriendid(userid, friendid)!=null){
    
    
            return 0;
        }
        //向喜欢表添加记录
        Friend friend = new Friend();
        friend.setUserid(userid);
        friend.setFriendid(friendid);
        friend.setIslike("0");
        friendDao.save(friend);

        //判断对方是否喜欢你,如果喜欢,就把isLike变成1
        if (friendDao.findByUseridAndFriendid(friendid,userid)!=null){
    
    
            friendDao.updateLike(userid,friendid,"1");
            friendDao.updateLike(friendid,userid,"1");
        }
        return 1;

    }


}

dao

public interface FriendDao extends JpaRepository<Friend,String> {
    
    
    
    /**
     * 根据用户ID与被关注用户ID查询记录个数,作用是为了查找该用户是否关注了另外一用户
     * @param userid
	 * @param friendid 
     * @return int
     */
    public Friend findByUseridAndFriendid(String userid,String friendid);

    /**
     * 更新为互相喜欢
     * @param userid
	 * @param friendid
	 * @param isLike 
     * @return void
     */
    @Modifying
    @Query(value="update tb_friend f set f.islike=?3 where f.userid = ?1 and f.friendid=?2",nativeQuery=true)
    public void updateLike(String userid,String friendid,String isLike);
}

在这里插入图片描述
在这里插入图片描述
当8也向2表示喜欢的时候
在这里插入图片描述
在这里插入图片描述

3、添加非好友(点击X,就是不喜欢该人)

dao:

public interface NoFriendDao extends JpaRepository<NoFriend,String> {
    
    
    
    /**
     * 根据用户ID与被关注用户ID查询记录个数,作用是为了查找该用户是否关注了另外一用户
     * @param userid
	 * @param friendid 
     * @return int
     */
    public NoFriend findByUseridAndFriendid(String userid,String friendid);

}

FriendService

    public int addNoFriend(String userid, String friendid) {
    
    
        //判断表中是否已经有该数据了
        NoFriend noFriend = noFriendDao.findByUseridAndFriendid(userid, friendid);
        if (noFriend!=null){
    
    
            return 0;
        }

        noFriend = new NoFriend();
        noFriend.setUserid(userid);
        noFriend.setFriendid(friendid);
        noFriendDao.save(noFriend);
        return 1;
    }

FriendController

    @PutMapping(value = "like/{friendid}/{type}")
    public Result addFriend(@PathVariable String friendid,@PathVariable String type){
    
    
    //xxxx
            //添加非好友
            else if (type.equals("2")){
    
    
                int flag = friendService.addNoFriend(userid, friendid);
                //重复添加非好友
                if (flag==0){
    
    
                    return new Result(false, StatusCode.ERROR,"重复点击了不喜欢");
                }
                //添加成功
                if (flag == 1) {
    
    
                    return new Result(true, StatusCode.OK,"添加成功");
                }
            }

ID8点击不喜欢ID5
在这里插入图片描述
在这里插入图片描述

4、粉丝数和关注数的变更

粉丝数和关注数变更,是在user模块中写的。所以我们要跨服务了。

1)user模块

controller

	/*
	 * 关注或者删除别人,自己的关注数+或者-1,别人的粉丝数+或者-1
	 * @param userid
	 * @param friendid
	 * @param x +1自己关注别人,-1自己删除别人
	 * @return void
	 */
	@PutMapping("{userid}/{friendid}/{x}")
	public void updateMyFollowcountAndHisFanscount(@PathVariable("userid") String userid,@PathVariable("friendid") String friendid,@PathVariable("x") int x){
    
    
		userService.updateMyFollowcountAndHisFanscount(userid,friendid,x);
	}

service

    public void updateMyFollowcountAndHisFanscount(String userid, String friendid, int x) {
    
    
		userDao.updateFollowcount(userid,x);
		userDao.updateFanscount(friendid,x);
    }

dao

@Transactional
public interface UserDao extends JpaRepository<User,String>,JpaSpecificationExecutor<User>{
    
    
	public User findByMobile(String mobile);

	/*
	 * 跟新自己的关注数
	 * @param userid
	 * @param x
	 * @return void
	 */
	@Modifying
	@Query(value="update tb_user set followcount=followcount+?2 where id =?1",nativeQuery=true)
	public void updateFollowcount(String userid, int x);

	/*
	 * 更新别人的粉丝数
	 * @param userid
	 * @param x
	 * @return void
	 */
	@Modifying
	@Query(value="update tb_user set fanscount=fanscount+?2 where id =?1",nativeQuery=true)
	public void updateFanscount(String friendid, int x);
}

2)交友模块调用user,进行粉丝数和关注数的修改

像上面一样,调用Feign
UserClient

@FeignClient("tensquare-user")
public interface UserClient {
    
    
    /*
     * 关注或者删除别人,自己的关注数+或者-1,别人的粉丝数+或者-1
     * @param userid
     * @param friendid
     * @param x +1自己关注别人,-1自己删除别人
     * @return void
     */
    @PutMapping("/user/{userid}/{friendid}/{x}")
    public void updateMyFollowcountAndHisFanscount(@PathVariable("userid") String userid, @PathVariable("friendid") String friendid, @PathVariable("x") int x);
}

friendController

        //添加好友
        if (type.equals("1")) {
    
    

            //根据不同情况添加好友
            int flag = friendService.addFriend(userid, friendid);
            //重复添加好友
            if (flag==0){
    
    
                return new Result(false, StatusCode.ERROR,"重复添加好友");
            }
            //添加成功
            if (flag == 1) {
    
    
            //修改关注数和粉丝数
                userClient.updateMyFollowcountAndHisFanscount(userid,friendid,1);
                return new Result(true, StatusCode.OK,"添加成功");
            }

        }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、删除好友

  1. 在friend表中删除对应数据
  2. 更新双方关注数和粉丝数
  3. 在nofriend表中添加数据

在这里插入图片描述

	/*
     *删除好友,在friend表中删除对应数据,更新双方关注数和粉丝数,在nofriend表中添加数据
     * @param friendid
     * @return entity.Result
     */
    @DeleteMapping("{friendid}")
    public Result deleteFriend(@PathVariable("friendid") String friendid){
    
    
        //验证登陆
        String token = (String) httpServletRequest.getAttribute("claims_user");
        if (token==null || "".equals(token)){
    
    
            //如果当前用户没有user角色
            return new Result(false, StatusCode.ERROR,"无权限操作");
        }
        //将token转义
        Claims claims = jwtUtil.parseJWT(token);
        String userid = claims.getId();


        friendService.deleteFriend(userid,friendid);
        userClient.updateMyFollowcountAndHisFanscount(userid,friendid,-1);
        return new Result(true,StatusCode.OK,"删除成功");

    }

service

    public void deleteFriend(String userid,String friendid) {
    
    
        //删除friend表中的数据
        friendDao.deleteFriend(userid,friendid);
        //更新friendid到userid的数据为0
        friendDao.updateLike(friendid,userid,"0");
        //nofriend表中添加数据
        NoFriend noFriend = new NoFriend();
        noFriend.setUserid(userid);
        noFriend.setFriendid(friendid);
        noFriendDao.save(noFriend);
    }

dao

    /*
     *根据userid和friendid进行删除
     * @param userid
     * @param friendid
     * @return void
     */
    @Query(value="delete from tb_friend where userid = ?1 and friendid=?2",nativeQuery=true)
    @Modifying
    public void deleteFriend(String userid, String friendid);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、熔断器Hystrix

1、概述

在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障, 进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种 因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
在这里插入图片描述
如该图,ABCD都是微服务,CD调用B,B调用A,但是如果A出错了或者A关闭了,B就也触发错误不可用了,就会导致CD也出错不可用了。
这个时候,熔断器Hystrix就出现了

2、什么是熔断器Hystrix

当A出错的时候,会返回一堆错误代码,因为微服务是同步而不是异步,就不会跳过这个错误继续进行下去了。而熔断器Hystrix就是当A出现错误的时候,返回一个Java可以识别的东西,比如类i,JSON之类的。可以让项目继续进行下去

3、QA调用Base模块

正常访问:
在这里插入图片描述
关掉base模块后
在这里插入图片描述
这里是做了所有异常返回result才这样,如果没有说明异常返回result,就会显示一堆奇奇怪怪的东西。

4、使用

1、yml

feign:
  hystrix:
    enabled: true

2、添加LabelClientImpl类

在clitnt-impl中添加

@Component
public class LabelClientImpl implements LabelClient {
    
    
    @Override
    public Result findById(String labelId) {
    
    
        return new Result(true, StatusCode.ERROR,"熔断器启动了");
    }
}

  • 记得将其加入容器中

3、修改LabelClient

修改@FeignClient参数

@FeignClient(value = "tensquare-base",fallback = LabelClientImpl.class)
public interface LabelClient {
    
    

    //这里要补全请求路径
    @GetMapping("/label/{labelId}")
    public Result findById(@PathVariable("labelId") String labelId);
}
  • @FeignClient(fallback = LabelClientImpl.class):如果触发了错误,返回LabelClientImpl类。如果突然好了,就返回正常请求,会自动检测的

4、效果

QA开,Base关
在这里插入图片描述
突然把Base开
在这里插入图片描述

六、网关Zuul

为什么要用网关?
我们用了那么多微服务,端口号都记不住了,base是9001,qa是9003.。。那么多记不住的。
在这里插入图片描述
这个图,所有微服务都是在eureka中,我们加入一个网关,用户用的时候,访问网关。网关我们自己设置,什么请求对应什么端口号,这样就不用记住了。

1、管理后台微服务网关

1)导包:

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

2)配置文件:

server:
  port: 9011
spring:
  application:
    name: tensquare-manager
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true #false只允许本地使用,true可以在服务器上使用

zuul:
  routes:
    tensquare-base: #
      path: /base/** #路径
      serviceId: tensquare-base #ID名
    tensquare-friend:
      path: /friend/**
      serviceId: tensquare-friend #ID名
    tensquare-gathering:
      path: /gathering/**
      serviceId: tensquare-gathering #ID名
    tensquare-qa:
      path: /qa/**
      serviceId: tensquare-qa #ID名
    tensquare-recruit:
      path: /recruit/**
      serviceId: tensquare-recruit #ID名
    tensquare-search:
      path: /search/**
      serviceId: tensquare-search #ID名
    tensquare-spit:
      path: /spit/**
      serviceId: tensquare-spit #ID名
    tensquare-user:
      path: /user/**
      serviceId: tensquare-user #ID名
    tensquare-sms:
      path: /sms/**
      serviceId: tensquare-sms #ID名

3)application

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ManagerApplication {
    
    

	public static void main(String[] args) {
    
    
		SpringApplication.run(ManagerApplication.class, args);
	}

}

4)使用方法

开启eureka、base、manager,这时候,Label有两种访问方法

①localhost:9001/label

在这里插入图片描述
这样子就是普通地访问全部数据

②localhost:9011/base/label

通过网关访问
在这里插入图片描述
这里简单理解为localhost:9011/base/取代了localhost:9001/

2、前台网关微服务

和管理员后台微服务网关一样 ,改下端口号9012

3、Zuul过滤器

@Component
public class ManagerFilter extends ZuulFilter {
    
    
    /**
     *  
     * @param  
     * @return pre 前置过滤器
     *          route 请求时调用
     *          post路由请求和error过滤器之后调用
     *          error处理请求时发生错误时调用
     */
    @Override
    public String filterType() {
    
    
        return "pre";
    }

    /**
     * 过滤器级别,越小越先执行
     * @param  
     * @return int
     */
    @Override
    public int filterOrder() {
    
    
        return 0;
    }

    /**
     * 是否开启过滤器
     * @param  
     * @return boolean
     */
    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }

    /**
     * 具体逻辑 
     * @param  
     * @return java.lang.Object
     */
    @Override
    public Object run() throws ZuulException {
    
    
        System.out.println("经过了Zuul过滤器");
        return null;
    }
}

在这里插入图片描述

4、用网关会导致头信息丢失

做个测试,比如get的label请求

    @GetMapping("")
    public Result findAll(){
    
    
        //测试
        String header = httpServletRequest.getHeader("Authorization");
        System.out.println(header);

        return new Result(true, StatusCode.OK,"查询成功",labelService.findAll());
    }

测下是否有头信息

1)不经过网关

在这里插入图片描述

asdasdasdasd
Hibernate: select label0_.id as id1_0_, label0_.count as count2_0_, label0_.fans as fans3_0_, label0_.labelname as labelnam4_0_, label0_.recommend as recommen5_0_, label0_.state as state6_0_ from tb_label label0_

2)经过网关

在这里插入图片描述

null
Hibernate: select label0_.id as id1_0_, label0_.count as count2_0_, label0_.fans as fans3_0_, label0_.labelname as labelnam4_0_, label0_.recommend as recommen5_0_, label0_.state as state6_0_ from tb_label label0_

经过网关后,数据就丢失了?!!

5、解决用网关丢失头消息问题(web)

先改YML

zuul:
  sensitive-headers:
  • 如果不加sensitive-headers,就会把authorization、set-cookie、cookie、host、connection、content-length、content-encoding、server、transfer-encoding、x-application-context给过滤掉,所以要把该值设为空

再改过滤器中的run方法

    @Override
    public Object run() throws ZuulException {
    
    
        //得到request上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
        //得到request域
        HttpServletRequest request = requestContext.getRequest();
        //得到头消息
        String header = request.getHeader("Authorization");
        //判断是否有该头消息
        if (header != null && !"".equals(header)) {
    
    
            requestContext.addZuulRequestHeader("Authorization",header);
        }

        return null;
    }

在这里插入图片描述

qqqqqqqqqq
Hibernate: select label0_.id as id1_0_, label0_.count as count2_0_, label0_.fans as fans3_0_, label0_.labelname as labelnam4_0_, label0_.recommend as recommen5_0_, label0_.state as state6_0_ from tb_label label0_

6、后台网关验证权限

这就是为了双层验证而已啦,加强安全。上面做的虽然有token转法到微服务,user可以这么做,但是admin不能这么做,要严格一点。

导包tensquare_commom包,还有JKD旧版本的包

        <dependency>
            <groupId>org.example</groupId>
            <artifactId>tensquare_commom</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
<!--        太高版本了,要下回旧版本的东西-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

加yml

jwt:
  config:
    key: tensquare

添加Bean

	@Bean
	public JwtUtil jwtUtil(){
    
    
		return new JwtUtil();
	}

1)过滤器

/**
 * @description:TODO
 */
@Component
public class ManagerFilter extends ZuulFilter {
    
    

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public String filterType() {
    
    
        return "pre";
    }

    @Override
    public int filterOrder() {
    
    
        return 0;
    }

    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }

    /**
     * 具体逻辑,安全验证,返回null就是放行,requestContext.setSendZuulResponse(false); 就是终止运行
     * @param
     * @return java.lang.Object
     */
    @Override
    public Object run() throws ZuulException {
    
    
        //得到request上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
        //得到request域
        HttpServletRequest request = requestContext.getRequest();

        //我也不懂。。。
        if (request.getMethod().equals("OPTIONS")) {
    
    
            return null;
        }

        //登陆的时候不做拦截
        //查找改请求路径是否有login字段,有的话就放行
        if (request.getRequestURI().indexOf("login")>0) {
    
    
            return null;
        }

        //得到头消息
        String header = request.getHeader("Authorization");
        //判断是否有该头消息且Bearer 开头
        if (header != null && !"".equals(header)&&header.startsWith("Bearer ")) {
    
    
        	//从第7个开始
            String token = header.substring(7);

            try {
    
    
                //转换
                Claims claims = jwtUtil.parseJWT(token);
                //如果不是空
                if (claims!=null){
    
    
                    //得到角色
                    if ("admin".equals(claims.get("roles"))){
    
    
                        //转发
                        requestContext.addZuulRequestHeader("Authorization",header);
                        return null;//null放行
                    }
                }
            }catch (Exception e){
    
    
                e.printStackTrace();
                requestContext.setSendZuulResponse(false);//终止运行
            }

        }

        //没有admin权限的话
        requestContext.setSendZuulResponse(false);//终止运行
        requestContext.setResponseStatusCode(401);//http状态码
        requestContext.setResponseBody("无权访问");
        requestContext.getResponse().setContentType("text/html;charset=UTF-8");

        return null;
    }
}

2)效果

错误
在这里插入图片描述
正确
在这里插入图片描述

七、集中配置组件SpringCloudConfig

1、概述

在这里插入图片描述
现在可以变成这种,代码放在内部公司服务器中,配置文件放在网上,反正别人只拿配置文件也没用,这样做的优势就是,配合SpringCloudBus使用,上线后,修改参数可以在网上修改而不用重新打开编译一遍。

2、流程

以base模块中的yml放在gitee上,使用SpringCloudConfig使本地代码读取gitee中的yml进行使用。

1)上传yml文件到gitee

文件名是有讲究的:
{application}-{profile}.yml{application}-{profile}.properties

  • application为应用名称,profile指的开发环境(用于区分开发环境,测试环境、生产环境 等),例子为base-dev.yml,就是base应用的开发环境

2)配置中心微服务

这个的yml不用放在网上,放在本地,其他的微服务可放网上
导包

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

yml

server:
  port: 12000

spring:
  application:
    name: tensquare-config
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/E-10000/tensquare.git

  • uri就是gitee对应仓库的位置

启动类

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ConfigApplication.class,args);
    }
}

http://localhost:12000/base-dev.yml:
在这里插入图片描述
这里可以读到yml了呢

3)配置客户端

以base为例子的,导包

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

添加bootstrap.yml(名字固定的),删除application.yml

spring:
  cloud:
    config:
     #base-dev.yml
      name: base
      profile: dev
      uri: http://localhost:12000
  • bootstrap级别比application高,如果同时存在,优先指定bootstrap。
  • base-dev,对应name: baseprofile: dev,所以名字不是随便起的

以web网关服务(该网关yml暂时没到网上)调用base服务
在这里插入图片描述

八、消息总线组件SpringCloudBus

SpringCloudConfig 将配置文件上传到网上,SpringCloudBus就类似一个热部署。放再网上修改配置文件的内容后,发送一个请求,就会自动更新了,而不用再次重启应用。(运维福音)

这里还是以base为例子,修改base中的配置参数

1、服务端(tensquare_config),修改yml

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
server:
  port: 12000

spring:
  application:
    name: tensquare-config
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/E-10000/tensquare.git
          force-pull: true
  rabbitmq:
    host: 192.168.12.128
    username: guest
    password: guest
    port: 5672
management:
  endpoints:
    web:
      exposure:
        include: "*"
#        include: bus-refresh
  • force-pull: true:强制更新
  • 这个用到rabbitmq,所以要写圈
  • management往下这部分,照抄就行

2、客户端导包(tensquare_base)

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>

yml补充:

server:
  port: 9001
spring:
  application:
    #    微服务通过application-name来进行连接,不能用_
    name: tensquare-base
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.12.128/tensquare_base?characterEncoding=UTF-8
    username: root
    password: root
  jpa:
    show-sql: true
    database: MySQL
  rabbitmq:
    host: 192.168.12.128
    username: guest
    password: guest
    port: 5672
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    prefer-ip-address: true #false只允许本地使用,true可以在服务器上使用
test: 这个B不会真的更新了吧
  • rabbitmq补充完整
  • 最后的test是我们自己写的,我们就是要用bus热部署使它更新

3、效果

1)在base的控制类中修改下方法

@RestController
@CrossOrigin
@RequestMapping("/label")//基础请求都是localhost:9001/label
@RefreshScope
public class LabelController {
    
    

    @Value("${test}")
    private String test;

    @Autowired
    private LabelService labelService;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @GetMapping("")
    public Result findAll(){
    
    
        //测试
        String header = httpServletRequest.getHeader("Authorization");
        System.out.println(test);
        return new Result(true, StatusCode.OK,"查询成功",labelService.findAll());
    }
  • @RefreshScope所有控制类都要加这个

2)运行localhost:9001/label,显示

这个B不会真的更新了吧

3)此时,更新yml中test的数据

test: 阿巴阿巴阿巴

4)然后用post请求的127.0.0.1:12000/actuator/bus-refresh,进行刷新

  • 没错,还是要进行手动刷新,还没有自动刷新呢
    在这里插入图片描述

5)然后再运行localhost:9001/label,显示

阿巴阿巴阿巴

猜你喜欢

转载自blog.csdn.net/yi742891270/article/details/110440209