• SpringCloud框架搭建+实际例子+讲解+系列五


    (4)服务消费者,面向前端或者用户的服务

    本模块涉及到很多知识点:比如Swagger的应用,SpringCloud断路器的使用,服务API的检查、token的校验,feign消费者的使用。大致代码框架如下:

     

    先看下简单的配置文件application.properties

    spring.application.name=mallservice-app
    server.port=4444
    eureka.client.serviceUrl.defaultZone=http://server1:1111/eureka/,http://server2:1112/eureka/,http://server3:1113/eureka/
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:5000
    urifilter.properties

    #urllist
    url.filterList[0]=/acc/signup
    url.filterList[1]=/acc/login
    面向用户的Controller类:

    package com.mallapp.api;

    import com.common.constant.RestApiResult;
    import com.common.constant.ReturnCode;
    import com.google.gson.Gson;
    import com.mallapp.Security.JWTUtils;
    import com.mallapp.client.IAccountFeignClient;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.UUID;

    @Api(value="用户服务",tags = "用户服务接口")
    @RestController
    @RequestMapping("/acc")
    public class IAccountController {
    @Autowired
    IAccountFeignClient accountFeignClient;


    @ApiOperation(value="用户注册")
    @RequestMapping(value="signup",method = RequestMethod.POST)
    public RestApiResult signUp(@RequestParam String phone, @RequestParam String password){
    RestApiResult restApiResult = new Gson().fromJson(accountFeignClient.signUp(phone,password),RestApiResult.class);
    System.out.println(restApiResult);
    return restApiResult;
    }
    @ApiOperation(value="用户登录")
    @RequestMapping(value="login",method = RequestMethod.POST)
    public RestApiResult login(@RequestParam String phone ,@RequestParam String password){
    RestApiResult restApiResult = new Gson().fromJson(accountFeignClient.login(phone,password),RestApiResult.class);
    try{
    System.out.println(restApiResult);
    if (restApiResult.isSuccess()){
    String accessToken = JWTUtils.createJWT(UUID.randomUUID().toString(),(String)restApiResult.getAddmessage(),2*60*60*1000);
    restApiResult.setAddmessage(accessToken);
    }
    }catch (Exception ex){
    ex.printStackTrace();
    }
    return restApiResult;
    }
    }
    @Autowired
    IAccountFeignClient accountFeignClient;
     这个是服务发现用的Feign的客户端,看一下它的实现:

    package com.mallapp.client;

    import com.mallapp.client.hystrix.AccountFeignClientHystrix;
    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;

    @FeignClient(name="ACCOUNT-SERVICE", fallback = AccountFeignClientHystrix.class)
    public interface IAccountFeignClient {
    @RequestMapping(value = "/acc/signup",method = RequestMethod.GET)
    public String signUp(@RequestParam(value = "phone") String phone, @RequestParam(value = "password") String password);
    @RequestMapping(value = "/acc/login",method = RequestMethod.POST)
    public String login(@RequestParam(value = "phone") String phone, @RequestParam(value = "password") String password);
    }
    这个接口必须和服务提供端的controller类的接口完全一致,而且参数注解一定完全一致。

    看下SpringCloud所说的断路器类的实现:(意义就是服务消费者端调用服务提供端的时候,调用超时或者服务器异常等,会直接通过此接口返回响应)

    package com.mallapp.client.hystrix;

    import com.common.constant.RestApiResult;
    import com.common.constant.ReturnCode;
    import com.google.gson.Gson;
    import com.mallapp.client.IAccountFeignClient;
    import org.springframework.stereotype.Component;

    @Component
    public class AccountFeignClientHystrix implements IAccountFeignClient {
    @Override
    public String signUp(String phone, String password) {
    return new Gson().toJson(new RestApiResult(false, ReturnCode.SYSTEM_ERROR,"The server is busy now......"));
    }

    @Override
    public String login(String phone, String password) {
    return new Gson().toJson(new RestApiResult(false, ReturnCode.SYSTEM_ERROR,"The server is busy now......"));
    }
    }


    看下所说的AOP中的前置通知、后置通知、环绕通知等实现类:

    package com.mallapp.aop;

    import com.common.constant.RestApiResult;
    import com.common.constant.ReturnCode;
    import com.mallapp.Security.JWTUtils;
    import io.jsonwebtoken.ExpiredJwtException;
    import io.jsonwebtoken.MalformedJwtException;
    import io.jsonwebtoken.SignatureException;
    import io.jsonwebtoken.UnsupportedJwtException;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;

    import javax.servlet.http.HttpServletRequest;
    import java.util.Iterator;
    import java.util.Map;

    @Aspect
    @Component
    public class ApiExecuteNoticeService {
    private final static Logger LOG = LoggerFactory.getLogger(ApiExecuteNoticeService.class);
    private final static String access_token = "accessToken";


    /**
    * 方法之前执行
    * @param joinPoint
    * @throws Exception
    */
    @Before("execution(public * com.mallapp.api.*.*(..))")
    public void doBeforeInService(JoinPoint joinPoint)throws Exception{
    System.out.println("Before to check the API......");
    }

    /**
    * 方法之后执行
    * @param joinPoint
    * @throws Exception
    */
    @After("execution(public * com.mallapp.api.*.*(..))")
    public void AfterInService(JoinPoint joinPoint)throws Exception{
    System.out.println("After to check the API......");
    }

    /**
    * 环绕通知
    * @param joinPoint
    * @return
    * @throws Exception
    */
    @Around("execution(public * com.mallapp.api.*.*(..))")
    public RestApiResult doAroundInService(ProceedingJoinPoint joinPoint)throws Exception{
    System.out.println("Around to check the API......");
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)requestAttributes;
    HttpServletRequest request = servletRequestAttributes.getRequest();
    String requestPath = request.getRequestURI();
    System.out.println("uri: " + requestPath);
    /*需要过滤不进行检查的url地址*/
    // if (requestPath.contains("acc")){
    // try {
    // return (RestApiResult)joinPoint.proceed();
    // } catch (Throwable throwable) {
    // throwable.printStackTrace();
    // }
    // System.out.println("url /acc does not to check.");
    // return null;
    // }
    Map<String,String[]> inputMap = request.getParameterMap();
    Iterator<String> keyIter = inputMap.keySet().iterator();
    boolean result = false;
    while(keyIter.hasNext()){
    String currKey = keyIter.next();
    String value = ((String[])inputMap.get(currKey))[0].toString();
    if (!access_token.equals(currKey)){
    continue;
    }
    try{
    JWTUtils.parseJWT(value);
    System.out.println("cuurKey="+currKey+",value="+value);
    result = true;
    }catch(ExpiredJwtException ex){
    ex.printStackTrace();
    }catch (UnsupportedJwtException ex){
    ex.printStackTrace();
    }catch (MalformedJwtException ex){
    ex.printStackTrace();
    }catch (SignatureException ex){
    ex.printStackTrace();
    }catch (IllegalArgumentException ex){
    ex.printStackTrace();
    }
    }
    if (!result){
    return new RestApiResult(false,ReturnCode.INVALID_VALUE,"token校验失败.");
    }
    try {
    return (RestApiResult) joinPoint.proceed();
    } catch (Throwable throwable) {
    throwable.printStackTrace();
    }
    return new RestApiResult(false,ReturnCode.SYSTEM_ERROR,"unkonwn exception");
    }
    }
     token校验所涉及到类:

    package com.mallapp.Security;

    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;

    import io.jsonwebtoken.*;
    import org.apache.tomcat.util.codec.binary.Base64;

    import java.util.Date;
    import java.util.UUID;

    public class JWTUtils {
    private final static String SECRETKEY = "OVlpXYjNwaFJYUllVbXhXTkZaR1pEQlNiVkYzWTBac1YxWkZXbE";
    /**
    * 由字符串生成加密key
    */
    public static SecretKey generateKsy(String keyStr){
    byte[] encodeKey = Base64.decodeBase64(keyStr);
    SecretKey secretKey = new SecretKeySpec(encodeKey,0,encodeKey.length,"AES");
    return secretKey;
    }
    /**
    * 创建JWT,加密过程
    */
    public static String createJWT(String id,String subject,long ttlMillis)throws Exception{
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    long nowMillis = System.currentTimeMillis();
    Date now = new Date(nowMillis);
    SecretKey key = generateKsy(SECRETKEY);
    JwtBuilder jwtBuilder = Jwts.builder().setIssuer("").setId(id).setIssuedAt(now).setSubject(subject)
    .signWith(signatureAlgorithm,key);
    if (ttlMillis >= 0){
    long expireMillis = nowMillis + ttlMillis;
    Date expireDate = new Date(expireMillis);
    jwtBuilder.setExpiration(expireDate);
    }
    return jwtBuilder.compact();
    }
    /**
    * 解析JWT,解密过程
    */
    public static Claims parseJWT(String jwt) throws ExpiredJwtException,UnsupportedJwtException,MalformedJwtException,
    SignatureException,IllegalArgumentException{
    SecretKey key = generateKsy(SECRETKEY);
    Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();
    return claims;
    }

    // public static void main(String[] args){
    // try{
    // String token = createJWT(UUID.randomUUID().toString(),"",20000);
    // System.out.println(token);
    // Claims claims = parseJWT(token);
    // System.out.println(claims.getExpiration()+" : "+claims.getExpiration().getTime());
    // }catch (Exception ex){
    // ex.printStackTrace();
    // }
    // }
    }
     
    UriFilterConfig类是用来接受Spring配置的xml文件的:urlifilter.properties
       

    package com.mallapp.config;

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.stereotype.Component;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Set;

    /**
    * Created by c00415904 on 2018/5/29.
    */
    @Component
    @ConfigurationProperties(prefix = "url")
    @PropertySource(value = {"classpath:urifilter.properties"} ,ignoreResourceNotFound = true)
    public class UriFilterConfig {
    private List<String> filterList = new ArrayList<String>();
    public List<String> getFilterList() {
    return filterList;
    }

    public void setFilter(List<String> filterList) {
    this.filterList = filterList;
    }
    }
    Awagger2Config类用来生成在线API文档: http://127.0.0.1:4444/swagger-ui.html 4444为消费者提供的端口号
    package com.mallapp.config;

    import io.swagger.annotations.ApiOperation;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;

    @Configuration
    @EnableSwagger2
    public class Awagger2Config {
    @Bean
    public Docket createRestApi(){
    return new Docket(DocumentationType.SWAGGER_2).apiInfo(getApiInfo()).select()
    .apis(RequestHandlerSelectors.basePackage("com.mallapp.api"))
    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
    .paths(PathSelectors.any())
    .build();
    }
    private ApiInfo getApiInfo(){
    return new ApiInfoBuilder().title("Mall App Swagger Apis").description("For mall-service 's app use")
    .version("V1.0").build();
    }
    }

    服务启动类:

    FeignApplication

    package com.mallapp;

    import com.common.constant.SystemConstant;
    import com.common.util.JedisUtil;
    import com.mallapp.config.UriFilterConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.feign.EnableFeignClients;

    import java.util.Date;

    @SpringBootApplication
    @EnableFeignClients
    @EnableEurekaClient
    @EnableDiscoveryClient
    public class FeignApplication implements CommandLineRunner{
    @Autowired
    private UriFilterConfig uriFilterConfig;
    public static void main(String[] args){
    SpringApplication.run(FeignApplication.class,args);
    }
    @Override
    public void run(String... strings) throws Exception {
    System.out.println("Begin to init data......"+new Date());
    System.out.println(uriFilterConfig.getFilterList());
    for(String url : uriFilterConfig.getFilterList()){
    JedisUtil.SETS.sadd(SystemConstant.URL_NEED_CHECK_KEY,url);
    }
    }
    }

    我们分别启动服务消费者和服务提供者,然后进行postman测试或者前端测试:

     

  • 相关阅读:
    SQLServer控制用户访问权限表
    jQuery 增加 删除 修改select option .
    C# Socket编程笔记
    前端笔记知识点整合之JavaScript(二)关于运算符&初识条件判断语句
    推荐一款中国风React组件
    Linux命令
    不要总想着二进制
    React 中阻止事件冒泡的问题
    [技术] 如何正确食用cnblogs的CSS定制
    新手如何理解JS面向对象开发?
  • 原文地址:https://www.cnblogs.com/huangwentian/p/10469196.html
Copyright © 2020-2023  润新知