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

目标

假设我们的系统中有3个微服务,分别为business-provider-svc、business-consumer-svc、其他服务。
现在的需求是不允许其他服务或着直接调用business-provider-svc,只能通过business-consumer-svc间接调用。
【微服务】自定义注解配合Spring拦截器实现服务调用鉴权_第1张图片

实现过程

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

你可能感兴趣的:(微服务,spring,微服务,java)