系统安全性和保密性设计

这个话题很大,我只是把我经历的或者说知道的,写一写,总总结,我并不是这方面的高手。
1 安全基础
1.1 国密算法
国家商用密码定义了一系列算法,我了解到的是SM2、SM3、SM4,因为国家对一些系统有安全要求,必须通过支持这三种算法,颁布相应授权证书。国密算法(国家商用密码算法简介)
SM2是替代RSA的算法,算法流程参考图解SM2算法流程(合
几个算法对比可以参见 国密算法SM1/SM2/SM3/SM4,我并不关心算法的实现,因为我只需要购买加密机就可以了,只要加密机支持这些算法,对接加密机就可以了。
从下图很容易想到几个问题
1、根密钥如何保证安全
应用系统使用的密钥是分散出来的,那么根密钥很容易想到也应该做分散。然后就是根密钥的灌装了。
2、这个前置与加密机之前的通讯安全如何保障?
应用系统不直连加密机,那么就需要设计一个前置系统。那么前置系统与机密机之间的安全设计呢?从下图可以看到加密机管理控制台有哪些功能,
这里面可以想象到加密机的前置系统,与加密机之间的接口通讯,采用了对称了加密,他们在内网,通过设置ip白名单进行授信。
2
3、应用系统通过前置与加密机通信?
前置和加密机也大都会采用对成加密,但是接口报文的加、解密是在交给加密机来处理,所以报文是安全的,而被截获的报文,即使知道了应用与加密机的加密密钥,那么也是密文,还是不知道如何解密。
1
1.2 MD5、SHA摘要算法
摘要算法很厉害,因为他不可逆,计算一个文件是否更改,只要看看他的摘要是否发生变化就可以。我的应用场景主要是在登录的时候使用。第2.3.3章 WEB系统最佳实践属性配置之shiro.properties第2.1.7章 WEB系统最佳实践Spring文件配置之spring-shiro.xml这两篇文章提到我的计算使用的是SHA-1,应用的shiro权限框架,这里稍微带一带。下图的密码流程,很容易就想到几个问题
1、客户端做MD5值,再调用后台请求之前,MD5做几次呢?
客户端一定是计算摘要的,是不是选择MD5算法抛开一边。但是一次MD5是可以破解的,比如在线MD5破解,密码通常6位~20位,MD5值是32位,因此再做一次MD5值,就相当难破解了,所以客户端只需要做2次MD5就可以了。
2、SHA-1安全吗
2017年2月23日,CWI Amsterdam与Google宣布了一个成功的SHA-1碰撞攻击,我相信有人攻击我的代价和他的收益没那么大,所以可以放心使用了。但是shiro也可以支持sha2.
3、加盐值是怎么产生的?
给出8位的长度,随机生成16个字节的加盐值,不容易猜吧。

byte[] salt = DigestsUtil.generateSalt(saltSize);
record.setSalt(EncodesUtil.encodeHex(salt));

4、多少次迭代合适呢?
网上大多数采用的是1024,虽然我不知道为什么,应该够用了。
5、保存到数据库中的密码安全吗?
最终存放到数据的密码长度为40位,在数据库中是无法猜到用户的账号密码的,内贼是可以防住了。可是运维的权限呢,如果运维更改了账号和密码,临时登录进去,再改回来。这就需要数据库的审计功能了。
2
1.3 数字签名
数字签名是保障信息的不可抵赖性。现在的开放接口大多提供一个appId和appKey给到接入方,可以看看蚂蚁金赋开放平台的做法,照着做就是了。实际应用,大多会砍一砍。
从下面可以思考几个问题,既然是数字签名,那么其实算法是公开的,那么问题就在于如何让appKey保密。
1、appKey存放到哪里呢
很多时候appKey写到了后台配置文件中,如果人员离职,这个appKey就带走了,不过还好大多数开放平台,appKey都可以更新,appId是不变的。
2、前端后端分离的appKey暴露了怎么办?
比如使用springboot+vue做前后端分离或者js访问开放平台的,有些人把appKey写到了前端,直接暴露有人觉得很危险,于是想到了,将appKey做分散,或者js做了混淆,只是增加了难度,而那些做爬虫的人,才不关心你怎么封装的,直接抓包看请求,这就需要用到了数字证书了。
1
从下图可以看到密码传输到终端用户,为什么很多企业用户花钱需要手机短信把,因为解决了密码传输的问题。
1
appId作为参数传到后台

private <T extends AlipayResponse> RequestParametersHolder getRequestHolderWithSign(BatchAlipayRequest request) throws AlipayApiException {

       List<AlipayRequestWrapper> requestList = request.getRequestList();
       //校验接口列表非空
       if (requestList == null || requestList.isEmpty()) {
           throw new AlipayApiException("40", "client-error:api request list is empty");
       }

       RequestParametersHolder requestHolder = new RequestParametersHolder();

       //添加协议必须参数
       AlipayHashMap protocalMustParams = new AlipayHashMap();
       protocalMustParams.put(AlipayConstants.METHOD, request.getApiMethodName());
       protocalMustParams.put(AlipayConstants.APP_ID, this.appId);
       protocalMustParams.put(AlipayConstants.SIGN_TYPE, this.signType);
       protocalMustParams.put(AlipayConstants.CHARSET, charset);
       protocalMustParams.put(AlipayConstants.VERSION, request.getApiVersion());

       if (request.isNeedEncrypt()) {
           protocalMustParams.put(AlipayConstants.ENCRYPT_TYPE, this.encryptType);
       }

       //添加协议可选参数
       AlipayHashMap protocalOptParams = new AlipayHashMap();
       protocalOptParams.put(AlipayConstants.FORMAT, format);
       protocalOptParams.put(AlipayConstants.ALIPAY_SDK, AlipayConstants.SDK_VERSION);
       requestHolder.setProtocalOptParams(protocalOptParams);

       Long timestamp = System.currentTimeMillis();
       DateFormat df = new SimpleDateFormat(AlipayConstants.DATE_TIME_FORMAT);
       df.setTimeZone(TimeZone.getTimeZone(AlipayConstants.DATE_TIMEZONE));
       protocalMustParams.put(AlipayConstants.TIMESTAMP, df.format(new Date(timestamp)));
       requestHolder.setProtocalMustParams(protocalMustParams);

       //设置业务参数
       AlipayHashMap appParams = new AlipayHashMap();

       //构造请求主题
       StringBuilder requestBody = new StringBuilder();
       // 组装每个API的请求参数
       for (int index = 0; index < requestList.size(); index++) {
           AlipayRequestWrapper alipayRequestWrapper = requestList.get(index);
           AlipayRequest alipayRequest = alipayRequestWrapper.getAlipayRequest();
           Map<String, Object> apiParams = alipayRequest.getTextParams();
           apiParams.put(AlipayConstants.METHOD, alipayRequest.getApiMethodName());
           apiParams.put(AlipayConstants.APP_AUTH_TOKEN, alipayRequestWrapper.getAppAuthToken());
           apiParams.put(AlipayConstants.ACCESS_TOKEN, alipayRequestWrapper.getAccessToken());
           apiParams.put(AlipayConstants.PROD_CODE, alipayRequest.getProdCode());
           apiParams.put(AlipayConstants.NOTIFY_URL, alipayRequest.getNotifyUrl());
           apiParams.put(AlipayConstants.RETURN_URL, alipayRequest.getReturnUrl());
           apiParams.put(AlipayConstants.TERMINAL_INFO, alipayRequest.getTerminalInfo());
           apiParams.put(AlipayConstants.TERMINAL_TYPE, alipayRequest.getTerminalType());
           apiParams.put(AlipayConstants.BATCH_REQUEST_ID, String.valueOf(index));

           // 仅当API包含biz_content参数且值为空时,序列化bizModel填充bizContent
           try {
               if (alipayRequest.getClass().getMethod("getBizContent") != null
                   && StringUtils.isEmpty(appParams.get(AlipayConstants.BIZ_CONTENT_KEY))
                   && alipayRequest.getBizModel() != null) {
                   apiParams.put(AlipayConstants.BIZ_CONTENT_KEY,
                       new JSONWriter().write(alipayRequest.getBizModel(), true));
               }
           } catch (NoSuchMethodException e) {
               // 找不到getBizContent则什么都不做
           } catch (SecurityException e) {
               AlipayLogger.logBizError(e);
           }
           requestBody.append(new JSONWriter().write(apiParams, false));
           if (index != requestList.size() - 1) {
               requestBody.append(BATCH_API_DEFAULT_SPLIT);
           }
       }
       appParams.put(AlipayConstants.BIZ_CONTENT_KEY, requestBody.toString());

       // 只有新接口和设置密钥才能支持加密
       if (request.isNeedEncrypt()) {

           if (StringUtils.isEmpty(appParams.get(AlipayConstants.BIZ_CONTENT_KEY))) {

               throw new AlipayApiException("当前API不支持加密请求");
           }

           // 需要加密必须设置密钥和加密算法
           if (StringUtils.isEmpty(this.encryptType) || getEncryptor() == null) {

               throw new AlipayApiException("API请求要求加密,则必须设置密钥类型和加密器");
           }

           String encryptContent = getEncryptor().encrypt(
               appParams.get(AlipayConstants.BIZ_CONTENT_KEY), this.encryptType, this.charset);

           appParams.put(AlipayConstants.BIZ_CONTENT_KEY, encryptContent);
       }

       requestHolder.setApplicationParams(appParams);

       if (!StringUtils.isEmpty(this.signType)) {

           String signContent = AlipaySignature.getSignatureContent(requestHolder);
           protocalMustParams.put(AlipayConstants.SIGN,
               getSigner().sign(signContent, this.signType, charset));

       } else {
           protocalMustParams.put(AlipayConstants.SIGN, "");
       }
       return requestHolder;
   }

AlipaySignature这个类可以看到参数根据字母的顺序进行排序

public static String getSignContent(Map<String, String> sortedParams) {
       StringBuffer content = new StringBuffer();
       List<String> keys = new ArrayList<String>(sortedParams.keySet());
       Collections.sort(keys);
       int index = 0;
       for (int i = 0; i < keys.size(); i++) {
           String key = keys.get(i);
           String value = sortedParams.get(key);
           if (StringUtils.areNotEmpty(key, value)) {
               content.append((index == 0 ? "" : "&") + key + "=" + value);
               index++;
           }
       }
       return content.toString();
   }

DefaultSigner中包含了签名的方法

public String sign(String sourceContent, String signType, String charset) {
       String sign = null;
       try {
           sign = AlipaySignature.rsaSign(sourceContent, this.privateKey, charset, signType);
       } catch (AlipayApiException e) {
           throw new RuntimeException(e);
       }
       return sign;
   }

1.4 数字证书
私钥加密,公钥解密,这个大家都很容理解,自己写的证书,浏览器是不识别的,因为没有授权。很容易理解,你自己颁布的证书,只有你自己可以认,虽然也是对称的,但是并不是CA机构所认可。数字证书解决通讯安全的问题,也就是说抓包是被加密的。但是它并不能解决源头授信的问题。
下图是参考一个spring-cloud项目,生成公私钥对,以及公私钥对存储的流程。
java学习-AES加解密之AES-128-CBC算法是对称算法,这里可自行指定对称算法。按照下图的流程,公私钥的存储周期,可以通过redis的销毁机制来控制。这里可以思考几个问题
1、私钥存到密钥的加密因子在哪里?
这个是固定的,很多人也都会想到写到配置文件中。
2、对离职人员的防范
哔哩哔哩源码的泄露是来自内部员工,史上最大账号密码数据库泄露,41GB数据文件出现在暗网,这些案例可以看到,基本出问题来自内部。采用下图的方式,是可以一定程度上防范。因为公私钥对有生命周期,即使开发人员或者SE知道算法,但是生成过程并不会干预。
2
2 访问控制
2.1 系统之间
在这里插入图片描述
2.1.1 每30秒获取允许访问的清单
既然Eureka作为服务中心,调用者订阅服务即可,不用关心服务提供者是谁,思考一下:
1 为何获取这个允许放单的清单从哪里来呢?
ServiceAuthRestInterceptor这段代码可以看出这个配置实际上是从数据库中获取的,这是一个技术业务问题。

@Override
@Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       HandlerMethod handlerMethod = (HandlerMethod) handler;
       // 配置该注解,说明不进行服务拦截
       CheckClientToken annotation = handlerMethod.getBeanType().getAnnotation(CheckClientToken.class);
       IgnoreClientToken ignoreClientToken = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
       if (annotation == null) {
           annotation = handlerMethod.getMethodAnnotation(CheckClientToken.class);
       }
       if (annotation == null || ignoreClientToken != null) {
           return super.preHandle(request, response, handler);
       } else {
           String token = request.getHeader(serviceAuthConfig.getTokenHeader());
           try {
               IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
               String uniqueName = infoFromToken.getUniqueName();
               for (String client : serviceAuthUtil.getAllowedClient()) {
                   if (client.equals(uniqueName)) {
                       return super.preHandle(request, response, handler);
                   }
               }
           }catch(ClientTokenException ex){
               response.setStatus(HttpStatus.FORBIDDEN.value());
               logger.error(ex.getMessage(),ex);
               response.setContentType("UTF-8");
               response.getOutputStream().println(JSON.toJSONString(new BaseResponse(ex.getStatus(), ex.getMessage())));
               return false;
           }
           response.setStatus(HttpStatus.FORBIDDEN.value());
           response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
           response.getOutputStream().println(JSON.toJSONString(new BaseResponse(RestCodeConstants.EX_CLIENT_FORBIDDEN_CODE,"Client is Forbidden!")));
           return false;
       }
   }

2 拦截器
授权系统拦截

@Configuration("securityWebConfig")
@Primary
public class WebConfiguration extends WebMvcConfigurerAdapter {
   @Bean
   GlobalExceptionHandler getGlobalExceptionHandler() {
       return new GlobalExceptionHandler();
   }

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       /*
           增加服务权限烂机器
        */
       registry.addInterceptor(getServiceAuthRestInterceptor()).addPathPatterns("/**");
       /*
           增加用户权限拦截器
        */
       registry.addInterceptor(getUserAuthRestInterceptor()).addPathPatterns("/**");
       super.addInterceptors(registry);
   }

   /**
    * 配置服务权限拦截
    * @return
    */
   @Bean
   ServiceAuthRestInterceptor getServiceAuthRestInterceptor() {
       return new ServiceAuthRestInterceptor();
   }

   /**
    * 配置用户用户token拦截
    * @return
    */
   @Bean
   UserAuthRestInterceptor getUserAuthRestInterceptor() {
       return new UserAuthRestInterceptor();
   }
}

auth:
 serviceId: ace-auth
 user:
   token-header: Authorization
   limit-expire: 1440 # 一天过期
 client:
   id: your-system
   secret: 123456
   token-header: client-token
@Scheduled(cron = "0/30 * * * * ?")
   public void refreshAllowedClient() {
       log.debug("refresh allowedClient.....");
       BaseResponse resp = serviceAuthFeign.getAllowedClient(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
       if (resp.getStatus() == 200) {
           ObjectRestResponse<List<String>> allowedClient = (ObjectRestResponse<List<String>>) resp;
           this.allowedClient = allowedClient.getData();
       }
   }

2.1.2 access token
access token的产生使用到了一个第三方jar,通过私钥进行了加密,通过公钥进行解密.
在系统初始化的时候,公私钥就产生了。
1 token的产生

@Configuration
@Slf4j
public class AuthServerRunner implements CommandLineRunner {
   @Autowired
   private RedisTemplate<String, String> redisTemplate;
   private static final String REDIS_USER_PRI_KEY = "AG:AUTH:JWT:PRI";
   private static final String REDIS_USER_PUB_KEY = "AG:AUTH:JWT:PUB";
   private static final String REDIS_SERVICE_PRI_KEY = "AG:AUTH:CLIENT:PRI";
   private static final String REDIS_SERVICE_PUB_KEY = "AG:AUTH:CLIENT:PUB";

   @Autowired
   private KeyConfiguration keyConfiguration;

   @Autowired
   private AECUtil aecUtil;

   @Autowired
   private RsaKeyHelper rsaKeyHelper;

   @Autowired
   private GatewayRouteBiz gatewayRouteBiz;

   @Override
   public void run(String... args) throws Exception {
       boolean flag = false;
       if (redisTemplate.hasKey(REDIS_USER_PRI_KEY)&&redisTemplate.hasKey(REDIS_USER_PUB_KEY)&&redisTemplate.hasKey(REDIS_SERVICE_PRI_KEY)&&redisTemplate.hasKey(REDIS_SERVICE_PUB_KEY)) {
           try {
               keyConfiguration.setUserPriKey(rsaKeyHelper.toBytes(aecUtil.decrypt(redisTemplate.opsForValue().get(REDIS_USER_PRI_KEY).toString())));
               keyConfiguration.setUserPubKey(rsaKeyHelper.toBytes(redisTemplate.opsForValue().get(REDIS_USER_PUB_KEY).toString()));
               keyConfiguration.setServicePriKey(rsaKeyHelper.toBytes(aecUtil.decrypt(redisTemplate.opsForValue().get(REDIS_SERVICE_PRI_KEY).toString())));
               keyConfiguration.setServicePubKey(rsaKeyHelper.toBytes(redisTemplate.opsForValue().get(REDIS_SERVICE_PUB_KEY).toString()));
           }catch (Exception e){
               log.error("初始化公钥/密钥异常...",e);
               flag = true;
           }
       } else {
           flag = true;
       }
       if(flag){
           Map<String, byte[]> keyMap = rsaKeyHelper.generateKey(keyConfiguration.getUserSecret());
           keyConfiguration.setUserPriKey(keyMap.get("pri"));
           keyConfiguration.setUserPubKey(keyMap.get("pub"));
           redisTemplate.opsForValue().set(REDIS_USER_PRI_KEY, aecUtil.encrypt(rsaKeyHelper.toHexString(keyMap.get("pri"))));
           redisTemplate.opsForValue().set(REDIS_USER_PUB_KEY, rsaKeyHelper.toHexString(keyMap.get("pub")));
           keyMap = rsaKeyHelper.generateKey(keyConfiguration.getServiceSecret());
           keyConfiguration.setServicePriKey(keyMap.get("pri"));
           keyConfiguration.setServicePubKey(keyMap.get("pub"));
           redisTemplate.opsForValue().set(REDIS_SERVICE_PRI_KEY, aecUtil.encrypt(rsaKeyHelper.toHexString(keyMap.get("pri"))));
           redisTemplate.opsForValue().set(REDIS_SERVICE_PUB_KEY, rsaKeyHelper.toHexString(keyMap.get("pub")));
       }
       log.info("完成公钥/密钥的初始化...");
       List<GatewayRoute> gatewayRoutes = gatewayRouteBiz.selectListAll();
       redisTemplate.opsForValue().set(RedisKeyConstants.ZUUL_ROUTE_KEY, JSON.toJSONString(gatewayRoutes));
       log.info("完成网关路由的更新...");
   }
}
public String generateToken(IJWTInfo jwtInfo) throws Exception {
       return jwtHelper.generateToken(jwtInfo, keyConfiguration.getServicePriKey(), expire);
   }
<dependency>
          <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt</artifactId>
          <version>0.7.0</version>
      </dependency>
@Scheduled(cron = "0 0/10 * * * ?")
   public void refreshClientToken() {
       log.debug("refresh client token.....");
       BaseResponse resp = serviceAuthFeign.getAccessToken(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
       if (resp.getStatus() == 200) {
           ObjectRestResponse<String> clientToken = (ObjectRestResponse<String>) resp;
           this.clientToken = clientToken.getData();
       }
   }

 public IJWTInfo getInfoFromToken(String token) throws Exception {
      try {
          IJWTInfo infoFromToken = jwtHelper.getInfoFromToken(token, serviceAuthConfig.getPubKeyByte());
          Date current = infoFromToken.getExpireTime();
          if(new Date().after(current)){
              throw new ClientTokenException("Client token expired!");
          }
          return infoFromToken;
      } catch (ExpiredJwtException ex) {
          throw new ClientTokenException("Client token expired!");
      } catch (SignatureException ex) {
          throw new ClientTokenException("Client token signature error!");
      } catch (IllegalArgumentException ex) {
          throw new ClientTokenException("Client token is null or empty!");
      }
  }

2 token检查
这里可以看到拦截器,通过反编译,将带CheckClientToken 注解的请求都拦截下来了。加了注解,就需要验证token。

 @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      HandlerMethod handlerMethod = (HandlerMethod) handler;
      // 配置该注解,说明不进行服务拦截
      CheckClientToken annotation = handlerMethod.getBeanType().getAnnotation(CheckClientToken.class);
      IgnoreClientToken ignoreClientToken = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
      if (annotation == null) {
          annotation = handlerMethod.getMethodAnnotation(CheckClientToken.class);
      }
      if (annotation == null || ignoreClientToken != null) {
          return super.preHandle(request, response, handler);
      } else {
          String token = request.getHeader(serviceAuthConfig.getTokenHeader());
          try {
              IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
              String uniqueName = infoFromToken.getUniqueName();
              for (String client : serviceAuthUtil.getAllowedClient()) {
                  if (client.equals(uniqueName)) {
                      return super.preHandle(request, response, handler);
                  }
              }
          }catch(ClientTokenException ex){
              response.setStatus(HttpStatus.FORBIDDEN.value());
              logger.error(ex.getMessage(),ex);
              response.setContentType("UTF-8");
              response.getOutputStream().println(JSON.toJSONString(new BaseResponse(ex.getStatus(), ex.getMessage())));
              return false;
          }
          response.setStatus(HttpStatus.FORBIDDEN.value());
          response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
          response.getOutputStream().println(JSON.toJSONString(new BaseResponse(RestCodeConstants.EX_CLIENT_FORBIDDEN_CODE,"Client is Forbidden!")));
          return false;
      }
  }
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD,ElementType.TYPE})
public @interface CheckClientToken {
}

这里配置那些需要检查客户端token

@RestController
@RequestMapping("depart")
@CheckClientToken
@CheckUserToken
public class DepartController extends BaseController<DepartBiz,Depart> {}

token拦截器得到token后,如果token过期,那么就刷新token。那么客户端token是否过期呢,则需要在客户端调用时,设置token过期标志,那么带来的问题是,为什么客户端自己不去刷新token呢?这就是上图中的access token是1个小时刷新一次,那么当在调用的时候,刚好1个小时呢,不就出问题了,所以这里做了一下主动刷新token。

@Component
@Log
public class OkHttpTokenInterceptor implements Interceptor {
	@Autowired
	@Lazy
	private ServiceAuthUtil serviceAuthUtil;
	@Autowired
	@Lazy
	private ServiceAuthConfig serviceAuthConfig;
	@Autowired
	@Lazy
	private UserAuthConfig userAuthConfig;


	@Override
	public Response intercept(Chain chain) throws IOException {
		Request newRequest = chain.request()
				.newBuilder()
				.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
				.build();
		Response response = chain.proceed(newRequest);
		if(HttpStatus.FORBIDDEN.value()==response.code()){
			if(response.body().string().contains(String.valueOf(RestCodeConstants.EX_CLIENT_INVALID_CODE))){
				log.info("Client Token Expire,Retry to request...");
				serviceAuthUtil.refreshClientToken();
				newRequest = chain.request()
						.newBuilder()
						.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
						.header(serviceAuthConfig.getTokenHeader(),serviceAuthUtil.getClientToken())
						.build();
				response = chain.proceed(newRequest);
			}
		}
	    return response;
	}

}

2.1.3 获取公钥
获取公钥由系统自动完成,避免了人为干预。

@Scheduled(cron = "0 0/1 * * * ?")
    public void refreshUserPubKey(){
        BaseResponse resp = serviceAuthFeign.getUserPublicKey(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
        if (resp.getStatus() == HttpStatus.OK.value()) {
            ObjectRestResponse<byte[]> userResponse = (ObjectRestResponse<byte[]>) resp;
            this.userAuthConfig.setPubKeyByte(userResponse.getData());
        }
    }
    @Scheduled(cron = "0 0/1 * * * ?")
    public void refreshServicePubKey(){
        BaseResponse resp = serviceAuthFeign.getServicePublicKey(serviceAuthConfig.getClientId(), serviceAuthConfig.getClientSecret());
        if (resp.getStatus() == HttpStatus.OK.value()) {
            ObjectRestResponse<byte[]> userResponse = (ObjectRestResponse<byte[]>) resp;
            this.serviceAuthConfig.setPubKeyByte(userResponse.getData());
        }
    }

3 网络安全
3.1 VPN
VPN是企业网在internet等公共网络上的延伸,它通过一个私有的通道在公共网络上创建一个安全的私有连接。
五分钟搞懂内网和外网之间的通信的原理,先搞明白公网ip和私有ip,大家知道企业宽带很贵,首先因为他有固定的公网ip,很容易理解出他是独享的,因为运营商可以让多个家庭宽带使用同一个公网ip,企业宽带凭什么比民用宽带贵?有没有更便宜的企业宽带,一般企业用户都要求是双线。那么什么是双线呢?
什么是双线宽带,这里提到了南电信北网通的问题,也就是南北网络互通的问题。
有了固定ip,就可以解决异地机房通信的问题。先看一下深信服IPSec VPN快速配置文档,通过防火墙的配置,可以实现家庭宽带与企业网络建立虚拟专用网,说白话,就是企业网访问家庭网络,就像访问内网一样。思考几个问题
1、家庭宽带和家庭宽带是不是没法实现vpn呢?
没有固定ip,连对方是谁也不清楚,连通讯都建立不起来,又怎么建立转有通道呢,有防火墙也没有用。
2、企业网络与企业网络之前一定能建立VPN吗?
例如阿里云与企业网络建立vpn,怎么建立呢?
3、IPSec协议的工作原理是什么?
建立网络专用通道,那么很容易理解IPSec是网络层协议,IPSec简介VPN和IPsec协议
在这里插入图片描述
3.2 防火墙
防火墙监控到内网有一台机器一直在请求DNS服务器,分析:
1、先分析一下HTTP请求流程
一次完整的HTTP请求过程,域名解析会先搜索浏览器自身的DNS缓存,缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存。
如果通过程序请求,而不是浏览器,会有DNS缓存吗?再看看这篇文章一文搞懂DNS缓存,浏览器和应用程序以及IPS网络运营商都会对DNS进行缓存。如果后台程序只访问一个地址,为什么会一直发送DNS呢?
2、为什么总是在请求DNS服务器呢?
防火墙抓包,全是在请求DNS。什么情况才会导致不断请求DNS呢?如果后台程序有循环bug,一直在请求某个网站,那么是否DNS就请求频繁呢?
2
通过zabbix可以监控到网络的请求情况,那么
1、Incoming指的是什么呢?
2、Outgoing指的是什么呢?
3、网络流量算大吗?

2

发布了317 篇原创文章 · 获赞 168 · 访问量 46万+

猜你喜欢

转载自blog.csdn.net/warrah/article/details/92804023