【微服务】自定义注解配合Spring拦截器实现服务调用鉴权

目标

假设我们的系统中有3个微服务,分别为business-provider-svc、business-consumer-svc、其他服务。
现在的需求是不允许其他服务或着直接调用business-provider-svc,只能通过business-consumer-svc间接调用。
在这里插入图片描述

实现过程

business-provider-svc 模块

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Authorize {
    // allowed consumers
    String[] value();
}

创建拦截器

public class AuthorizeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Authorize authorize = handlerMethod.getMethod().getAnnotation(Authorize.class);
        if (authorize == null) {
            return true; // no need to authorize
        }

        String[] allowedHeaders = authorize.value();
        String authzHeader = request.getHeader("Authorization");

        if (StringUtils.isEmpty(authzHeader)) {
            throw new RuntimeException("Missing Authorization http header");
        }

        if (!Arrays.asList(allowedHeaders).contains(authzHeader)) {
            throw new RuntimeException("You do not have access to this service");
        }
        return true;
    }
}

配置拦截器

@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthorizeInterceptor());
    }
}

Controller、Service、Repository、Model相关代码

Controller

@RestController
@RequestMapping("/v1/business")
public class BusinessController {

    @Autowired
    private BusinessService businessService;

    @GetMapping("/save")
    @Authorize(value = {"business-consumer-svc"})
    public BusinessData save(){
        BusinessData businessData = new BusinessData();
        businessData.setLogInfo("query salary " + System.currentTimeMillis());
        businessData.setLogTime(new Date());
        BusinessData saved = businessService.save(businessData);
        return saved;
    }

    @GetMapping("/getList")
    @Authorize(value = {"business-consumer-svc"})
    public List<BusinessData> getList(){
        List<BusinessData> list = businessService.getList();
        return list;
    }

}

Service

@Service
public class BusinessService {

    @Autowired
    private BusinessRepository businessRepository;

    public BusinessData save(BusinessData businessData){
        BusinessData saved = businessRepository.save(businessData);
        return saved;
    }

    public List<BusinessData> getList(){
        List<BusinessData> lists = new ArrayList<>();
        Iterable<BusinessData> all = businessRepository.findAll();
        all.forEach(b -> lists.add(b));
        return lists;
    }
}

Repository

public interface BusinessRepository extends CrudRepository<BusinessData,String> {
}

model

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Entity(name = "business_data")
public class BusinessData {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name = "log_info")
    private String logInfo;
    @Column(name = "log_time")
    private Date logTime;
}

pom及配置文件

pom

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

    </dependencies>

配置文件

server:
  port: 8082
spring:
  application:
    name: business-provider-svc
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/boot_integrate_other
    username: root
    password: root
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update

business-provider-api 模块

定义Feign接口

@FeignClient(name = "business-provider-svc",path = "/v1/business",url = "http://localhost:8082")
public interface BusinessProviderClient {

    @GetMapping("/save")
    BusinessData save(@RequestHeader("Authorization") String authz);

    @GetMapping("/getList")
    List<BusinessData> getList(@RequestHeader("Authorization") String authz);

}

business-consumer-svc 模块

Controller

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private BusinessProviderClient businessProviderClient;

    @GetMapping("/testAuth")
    public List<BusinessData> testAuth(){
        List<BusinessData> list = businessProviderClient.getList("business-consumer-svc");
        return list;
    }

    @GetMapping("/testNoAuth")
    public List<BusinessData> testNoAuth(){
        List<BusinessData> list = businessProviderClient.getList("business-consumer-svc-2");
        return list;
    }
}

pom及配置文件

pom

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.xupengzhuang</groupId>
            <artifactId>business-provider-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

配置文件

server:
  port: 8081
spring:
  application:
    name: business-consumer-svc

代码全部编写完后,就可以启动两个module进行测试了。

完整的工程代码请访问:boot-anno-interceptor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java拦截器是一种应用程序设计模式,它可以截获并处理请求、响应和异常。Spring框架提供了一个拦截器接口HandlerInterceptor,让我们可以在请求到达Controller之前或者Controller返回结果之后进行一些自定义的操作,如权限控制、日志记录、性能监控等。 Spring拦截器实现步骤如下: 1.定义一个类实现HandlerInterceptor接口,并实现其中的三个方法:preHandle、postHandle和afterCompletion。这些方法分别对应请求处理前、请求处理后和请求完成后的操作。 2.在Spring的配置文件中配置拦截器,并指定拦截的路径。可以使用<mvc:interceptors>标签来配置拦截器,也可以使用@Bean注解来配置拦截器。 3.在Controller类中使用@RequestMapping注解来指定请求路径。 4.启动应用程序,访问配置的路径,观察拦截器的执行效果。 HandlerInterceptor接口提供了三个方法,分别是: 1.preHandle方法:在请求处理之前进行调用,返回值为boolean类型。如果返回true,则继续向下执行;如果返回false,则不会执行Controller中的方法。 2.postHandle方法:在请求处理之后进行调用,但是在视图被渲染之前。即Controller方法调用之后,但是DispatcherServlet向客户端响应视图之前执行。可以利用ModelAndView参数来传递数据。 3.afterCompletion方法:在整个请求完成之后,也就是视图渲染之后执行。一般用于资源清理操作。 在Spring框架中,拦截器的执行顺序与它们在配置文件中的顺序有关。如果有多个拦截器,那么它们的执行顺序是按照配置文件中定义的顺序依次执行的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值