Android开发经验注意点

Android开发经验注意点

1.I/O篇

1.1磁盘I/O

  • 项目要求不需要实时性和及时性尽可能的多用缓存,因为看你导致重复读写,在一些系统API中每次都可能需要重复打开文件导致磁盘IO读写性能浪费。

例如SharedPreferences的commit每一次的commit都进行了一次文件的打开和关闭,因此尽可能的使用一次commit方法即可。利用混存来保存多次写入的数据,延迟写入,从而减少写入次数。

  • 避免主线程进行读写,移到子线程中处理I/O事宜。因为有可能发生『放大效应』(简单来讲放大效应是在随机读写时需要清理当前磁盘的块整理的过程,和顺序读写不同在于整理过程需要耗费一定的性能导致明明只写8KB的大小却需要调度到512KB)会让平时是多毫秒的操作放大几十倍,导致主线程长时间未响应导致的体验不佳。但是数据是见面的来源,么有数据也无法展示相应的核心内容,所以移入子线程只是解决的第一步,最重要的是如何真正的减少I/O甚至避免I/O操作。
  • 合理的I/O缓存设置Buffer的大小。可以根据业务和机器性能环境来适当的调整,在读写时使用缓冲区开可以减少读写的次数,从而减少了切换内核态的次数提高效率。一般推荐Buffer大小为8KB。

阅读全文

项目中的思考

当初成为火排队的Owner的时候就不再以一个纯粹的的开发人员的职责来要求自己,或多或少会接触一些开发外的杂事,跟客户了解需求、跟其他开发兄弟协调任务、自己研究餐厅业务相关的知识、开始关注团队成长和氛围、用户体验、关注架构、关注未来可能的需求,尽可能保证一点前瞻性吧.

在做管理学习上我觉得有个简单的方法叫对标学习。在其位谋其政,不在其位预谋其政。看你现在的项目经理、技术总监、CTO的工作日常,有机会分担责任时,哪怕是协调人员、组织会议、跟踪任务。

一个项目的开发时间是固定的,甚至在大多数互联网公司来说时间是紧迫的 不够的。一个项目从开始到结束,参与的人除了客户端,还有测试和服务端,以及产品。每个人是否能够完成自己手头的任务? 需求的频繁变更,以及前期未料到的技术难点等等,这些都是不可控因素,任何一点没做好都有可能导致项目失控,最后的结果就是逾期。

  1. 很重要的一点就是任务分配。要充分了解小组内所有成员的特点,把任务分派到合适的人员头上。同时注意,一个模块的开发,如果资源允许的情况下最好安排给2个人协同开发,而不是一个模块一个人(里面有各种各样的情况大家想想可能就会懂了)。
  2. 任务细分。包括任务时间划定和任务描述,这是很有挑战性的操作,怎么在开发前准确理清任务的脉络,并在描述中写出来让相关人员避免踩坑,这是任务进度可控的关键所在。任务粒度要足够细才能在后面的周会和禅道上精准把控每个人的完成情况。
  3. 任务优先级。技术选型一定要提前展开,方案没出就可以开始。基础性任务,这个版本的重要需求任务,也要排到前面,原则就是先重后轻。
  4. 每周一次的例会是少不了的,不同开发阶段会议侧重点不一样。开发中期侧重就是了解每个开发人员的进度情况(包括服务端),沟通一些技术问题,作为组长要及时提醒一些逾期的任务。开发后期,则主要是明确变更的需求调整,对于大的改动应该再三商榷。另外,把握相关的测试进度,督促测试人员加大测试力度。
  5. 要实时关注开发进度,对于技术难点可以协助介入,如果长时间未能解决要及时停止,进行后面的开发。在后期在对这个难点灵活处理:比如暂时用特殊方法规避,或者和产品商议放到下个版本处理。
  6. 两个关键节点一定要守住:功能封板和0bug。功能封板是指客户端完成所有开发任务,能够全程无障碍交付测试,0bug则是发版的最后一道防线。

事情做到前面

要比别人花更多的精力,提前预判一些未知的问题:比如需求的不合理性,方案没有描述的地方,能够在开发前及时指出。服务端接口定义是够满足开发需求,也要提前审查。还有就是客户端开发的障碍扫平包括复杂模块的程序方案,一些技术研究,一些坑点等等。

你至少要比正常进度提前1-2天,把准备工作做好,一直保持最坏的打算的心态。

每天早上要了解组内成员和服务端的任务进度,必须时要运行并过一遍两端的app各模块。从我的经验来看,很多时候禅道上的任务进度有水分,明明功能没有完成,他要说自己已经完成了,这时候如果你不及时指出会直接导致后面0 bug节点失守!不要过分相信数据,要相信自己的眼睛(自己多做一会测试工作、不是不相信测试哥们,而是多一个『小白』测试也是一个测试想法要千奇百怪的测,毕竟一千个人心中有一千个哈姆雷特)。

还有就是ui这一块的工作安排,要提前和UI同时沟通确定任务时间,不要到客户端本地开发快完成了,才想起来还有ui切图没有给全。ui切图的按完成时间至少要比客户端本地开发截止日期提前1-2天,这样才能顺利完成所有的本地开发。

引导项目向前推动

每个人都是做自己的一亩三分地,服务端和产品可能还要负责其他的业务,大家很难做到100%的精力都投入到这个项目中。遇到一些冲突的地方就有可能是项目停滞不前,应该主动站出来组织一个短会让大家参与讨论,解决问题。

结果导向

大家应该没少听说 过程导向和结果导向这两个词,有时面试中也会被问起,两者的区别我不再赘述。我只想说,管理一个项目是绝对的结果导向。我不关心中间发生了什么,只要告诉我结果,以及你是否能够守住自己的承诺。 这同样也是小组长对自己的要求,老大既然愿意把这份责任交给你,那么一切以结果说话。0 bug时间节点到了,结果是android 和ios还有几十个bug。你可以找出十几个原因,但是毫无意义。如果你能在任务细分上做的更好,如果你在开发前期关注进度更多一点,是不可能导致现在的局面。多从自己身上找原因吧,我相信一句话:任何坏的结果不是在于过程中发生了什么,而是在于我们做了什么。

一些挑战

比较大的挑战主要是下面两个方面: 一是开发任务的细分。需要足够的开发经验积累,以及对需求的透彻理解,这个还是很难把控,毕竟在开发前就要把一个任务的来龙去脉,各种路径全部理清并不是一件容易事。但这个事情是很重要的,以我的经验来看任务细分的质量直接关系到功能开发的进度能否按时完成。

二是发版前期的进度把控,包括需求的变更和取舍,无法解决的技术难点处理。这里的准则就是:版本稳定为主,后期不应该引入较大的需求变更!比如3天后要发版了,那么接下来的3天的代码提交你要亲自审查,需求变更基本是拒绝的。如果现在还有谋和技术难点没有攻克,建议采取特殊处理办法暂时规避,后面再还这个技术债.

OkHttp3框架解析

OKHttp3网络框架解析

本文会先简单说下OkHttp3的工作流程,然后介绍OkHttp3的一些核心类(如连接池StreamAllocation以及各式各样的Interceptor),接着从源码角度分析一次HTTP请求在OkHttp3中所经历的过程,在不同的Interceptor(拦截器)可以看到一些OkHttp3设计的一些巧妙思想,最后对上述分析做个简单的总结。

Okhttp3是Square公司开源的强大高效的Java网络请求库,旨于替换Java的HttpUrlConnection和Apache的HttpClient的轻量级网络框架,已经被运用到很多开源库以及Android的源码中(Android Studio 在6.0之后,移除了HttpClient,并且用OKHttp代替了HttpUrlConnection)。

  • 支持Http2/SPDY;
  • 默认启用长连接,使用连接池管理,支持Cache(目前仅支持GET请求的缓存);
  • 路由节点管理,提升访问速度;
  • 透明的Gzip处理,节省网络流量。
  • 灵活的拦截器,行为类似Java EE的Filter或者函数编程中的Map。

阅读全文

Retrofit2框架解析

网络框架Retrofit解析

什么是Retrofit框架?

Retrofit其实我们可以理解为OkHttp的加强版,它也是一个网络加载框架。底层是使用OKHttp封装的。准确来说,网络请求的工作本质上是OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。它的一个特点是包含了特别多注解,方便简化你的代码量。并且还支持很多的开源库(著名例子:Retrofit + RxJava)。

本文中的Retrofit均指代Retrofit2.0,以目前最新的2.4.0版本为例子。

Retrofit入门

Retrofit 其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多。

Retrofit采用了建造者模式来保证这个类构建的确保了相应的默认值和规范性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}

我们使用Retrofit2分以下三步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 第一步构建Retrofit
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.weather.com.cn/").build();
// 第二步添加服务接口
ApiService service = retrofit.create(ApiService.class);
// 第三步执行请求 利用Retrofit创建我们的接口对象,生成Call对象,并调用请求网络方法
service.weather().enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
try {
tvTest.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {
tvTest.setText(t.getMessage());
}
});

第一步解析构建Retrofit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public Retrofit build() {
//这里可以看出我们必需设置baseUrl
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//如果我们没有设置自定义的OkHttpClient,就用默认的OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//这个callbackExecutor用于回调到UI线程
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//将我们设置的Call适配器添加到列表中,比如RxJava的适配器
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
//添加默认的Call适配器
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
//添加数据转换器,用于将网络请求返回的结果转换成我们需要的类型
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
//构建Retrofit
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

从上面可以看到
(1)为什么说Retrofit2是基于OKhttp3的封装,因为CallFactory其实调用的就是OKhttpClient,所以说Retrofit2是基于OkHttp3的封装。

(2)callbackExecutor回调执行器,这个是用于网络请求返回后回调到主线程的。我们知道,在利用OkHttp3进行网络请求时,我们需要手动回调到主线程,以便更新UI。那我们来看看这个默认的defaultCallbackExecutor是不是这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
...
static class Android extends Platform {
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}
}

从上面代码可以看到这个Platform的作用是判断当前作用的平台以及创建好主线程和进行回调。所以当我们调用platform的defaultCallbackExecutor时,实际上调用的是Android这个类的defaultCallbackExecutor方法,而这个方法返回的是MainThreadExecutor,它的作用就是利用handler将执行结果回调到主线程中。

(3)callAdapterFactories适配器列表,这里面存放的是所有的Call适配器CallAdapter。这个CallAdapter是干嘛的呢?我们知道Retrofit2底层是用Okhttp3来请求网络的,那势必是通过OkHttp3的Call来执行请求,但是我们在实际使用Retrofit2时,可能需要结合请他的三方库,常见的比如RxJava,那我们接口中所需要返回的可能就是Observable而不是Call了。

1
2
@GET("test/t")
Observable<WeatherBean> getObservableData();

所以Retrofit2就需要一个Call适配器CallAdapter,来将Call转换为我们需要的Observable,这就是CallAdapter的作用。

(4)converterFactories返回结果转换器列表,这里面存放的是将网络返回结果,也就是OkHttp3返回的Response,解析转换成我们需要的实际类型,比如WeatherBean实体类。如GsonConverterFactory就是一个常用的转换器,相当于Retrofit2把我们直接使用Okhttp3来请求网络数据时需要将返回结果进行Gson解析的动作也替我们做了。

第二步解析ApiService接口是如何转化为我们的接口请求调用的

在第二步骤中我们使用了retrofit.create添加服务接口

1
2
// 添加服务接口
ApiService service = retrofit.create(ApiService.class);

我们进一步看一下create方法到底做了什么事情看到以下代码其实做的事情明显,就是做了接口校验和创建动态代理。(不熟悉动态代理的童鞋可以去自行百度一下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public <T> T create(final Class<T> service) {
// 校验该类是不是接口以及校验该类必须不能继承自其他接口
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 进行动态代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
// 用来判断调用哪个平台的方法 并且创建线程池
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//这里默认返回是false,所以不会执行
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args);
}
});
}

最后返回的是loadServiceMethod方法我们再进去看看调用了什么东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
// 加入锁进行解析
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 重点是这一步的解析
result = ServiceMethod.parseAnnotations(this, method);
// 会将已经解析过的方法缓存起来
serviceMethodCache.put(method, result);
}
}
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abstract class ServiceMethod<T> {
// 这个抽象类似用来解析注解方法
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 这个工厂类似用来解析注解类比如Get、Post、Header等等之类的
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// 获取方法的返回类型 我们可能是Call<T> 可能是Observable<T>
Type returnType = method.getGenericReturnType();
// 校验一下这个返回类型能否解析
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
// 将解析完的retrofit和请求参数在经历一次封装进行CallAdapter的适配并且做一些默认操作例如参数和回调的类型转换等等
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 根据注解解析并且创建CallAdapter对象
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
// 获取解析的对象类型
Type responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// 校验规则
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// 根据返回的类型去Convert的列表中对应并且创建相应的Converter的对象
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
private final RequestFactory requestFactory;
private final okhttp3.Call.Factory callFactory;
private final CallAdapter<ResponseT, ReturnT> callAdapter;
private final Converter<ResponseBody, ResponseT> responseConverter;
private HttpServiceMethod(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
CallAdapter<ResponseT, ReturnT> callAdapter,
Converter<ResponseBody, ResponseT> responseConverter) {
this.requestFactory = requestFactory;
this.callFactory = callFactory;
this.callAdapter = callAdapter;
this.responseConverter = responseConverter;
}
@Override ReturnT invoke(@Nullable Object[] args) {
// 所以最后一步调用的是这个函数转化为OkHttpCall类来进行请求
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}

(1)第一步是使用创建动态代理进一步进行解析方法和配置
(2)第二步解析完方法并且根据前面的Retrofit配置进一步的解析注解来配置CallAdapter对象和Converter对象
(3)第三步解析完所有的方法和对象后创建OkHttpCall对象来返回进行网络请求

第三步进入网络请求

在回顾一下上面我们请求的是这个实际上调用的就是OkHttpCall的queue方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
service.weather().enqueue(new retrofit2.Callback<ResponseBody>() {
@Override
public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
try {
tvTest.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {
tvTest.setText(t.getMessage());
}
});

由于我们在Retrofit2里的默认Android的CallAdapterFactory配置转换是ExecutorCallAdapterFactory(详情可以去看Platform类里)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static class Android extends Platform {
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}

WX20180916-142652

然后我们来看以下OkHttpCall里的enqueue方法
WX20180916-143004

所以很明显里面实际上调用的就是Okhttp3的Call类来执行相应的网络请求。

总结

(1)这里我们分析了Retrofit的创建过程,可以发现Retrofit中核心部分包括OkHttpClient、Call适配器、Response转换器以及用于将请求回调到UI线程的回调执行器等。

(2)Retrofit的创建采用的是Builder模式,定制化程度很高,我们可以设置自己的OkHttpClient,也可以实现自定义的Call适配器和Response转换器,以及请求的回调执行器。

(3)可见Retrofit虽然做了很大程度的封装,但是灵活性扩展性依然很高,我们几乎可以扩展更改网络请求的大部分过程。

(4)Retrofit2中最主要的就是3个类:Retrofit、ServiceMethod和OkHttpCall。这三个类指责明确,相互配合共同完成整个网络调用的流程。

  • Retrofit负责供外部初始化和定制,保存CallAdapter的列表和ResponseConverterFactory列表。
  • ServiceMethod对应每一个接口方法的信息,包括解析注解和参数等,同时它也是连接Retrofit和OkHttpCall的桥梁。ServiceMethod中保存着当前接口对应方法所需要的CallAdapter和ResponseConverter。利用CallAdapter将OkHttpCall转换成接口需要的类型,供接口调用。利用toResponse方法让OkHttpCall调用ResponseConverter解析网络请求返回的结果。
  • OkHttpCall则是用来执行具体网络请求。Retrofit2没有直接使用OkHttp3的Call接口,而是有自己的Call接口。在OkHttpCall内部通过组合的方法持有OkHttp3的Call接口,并通过ServiceMethod的toCall方法得到OkHttp3的call来进行网络请求,减少对OkHttp3的耦合。

Api接口加密策略

Api接口加密策略

接口安全要求:
1.防伪装攻击(案例:在公共网络环境中,第三方 有意或恶意 的调用我们的接口)

2.防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容 在传输过程被修改)

3.防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放)

4.防数据信息泄漏(案例:截获用户登录请求,截获到账号、密码等)

设计原则:
1.轻量级

2.适合于异构系统(跨操作系统、多语言简易实现)

3.易于开发

4.易于测试

5.易于部署

6.满足接口安全需求(满足接口安全1,2,3),无过度设计。

其它:接口安全要求防数据信息泄漏部分,主要针对目前用户中心的登录接口

设计原则是:使用HTTPS安全协议 或 传输内容使用非对称加密,目前我们采用的后者。

适用范围:
1.所有写操作接口(增、删、改 操作)

2.非公开的读接口(如:涉密/敏感/隐私 等信息)

接口参数签名 实现思路参考:
必要的输入参数:

签名算法过程:

1.对除签名外的所有请求参数按key做的升序排列,value无需编码。 (假设当前时间的时间戳是12345678)

例如:有c=3,b=2,a=1 三个参,另加上时间戳后, 按key排序后为:a=1,b=2,c=3,_timestamp=12345678。

2 把参数名和参数值连接成字符串,得到拼装字符:a1b2c3_timestamp12345678

3 用申请到的appkey 连接到接拼装字符串头部和尾部,然后进行32位MD5加密,最后将到得MD5加密摘要转化成大写。

示例:假设appkey=test,md5(testa1b2c3_timestamp12345678test),取得MD5摘要值 C5F3EB5D7DC2748AED89E90AF00081E6 。

常见的加密方式:
DES加密算法: DES加密算法是一种分组密码,以64位为分组对数据加密,它的密钥长度是56位,加密解密用同一算法。DES加密算法是对密钥进行保密,而公开算法,包括加密和解密算法。这样,只有掌握了和发送方相同密钥的人才能解读由DES加密算法加密的密文数据。因此,破译DES加密算法实际上就是搜索密钥的编码。对于56位长度的密钥来说,如果用穷举法来进行搜索的话,其运算次数为256。

随着计算机系统能力的不断发展,DES的安全性比它刚出现时会弱得多,然而从非关键性质的实际出发,仍可以认为它是足够的。不过,DES现在仅用于旧系统的鉴定,而更多地选择新的加密标准。

AES加密算法: ES加密算法是密码学中的高级加密标准,该加密算法采用对称分组密码体制,密钥长度的最少支持为128、192、256,分组长度128位,算法应易于各种硬件和软件实现。这种加密算法是美国联邦政府采用的区块加密标准,这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

AES加密算法被设计为支持128/192/256位(/32=nb)数据块大小(即分组长度);支持128/192/256位(/32=nk)密码长度,,在10进制里,对应34×1038、62×1057、1.1×1077个密钥。

RSA加密算法: RSA加密算法是目前最有影响力的公钥加密算法,并且被普遍认为是目前最优秀的公钥方案之一。RSA是第一个能同时用于加密和数宇签名的算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA加密算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

Base64加密算法: Base64加密算法是网络上最常见的用于传输8bit字节代码的编码方式之一,Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在JAVAPERSISTENCE系统HIBEMATE中,采用了Base64来将一个较长的唯一标识符编码为一个字符串,用作HTTP表单和HTTPGETURL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。

MD5加密算法: MD5为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。对MD5加密算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成—个128位散列值。

MD5被广泛用于各种软件的密码认证和钥匙识别上。MD5用的是哈希函数,它的典型应用是对一段信息产生信息摘要,以防止被篡改。MD5的典型应用是对一段Message产生fingerprin指纹,以防止被“篡改”。如果再有—个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。MD5还广泛用于操作系统的登陆认证上,如UNIX、各类BSD系统登录密码、数字签名等诸多方

总结:
1、接口调用方和接口提供方约定好统一的参数加密算法

2、接口调用方在调用时把加密后的_sign放在参数中去请求接口

3、接口提供方接到响应后,判断时间戳是不是在有效时间内(这个时间间隔根据你的安全范围可以是10分钟,5分钟,20秒等,过期失效,前提是需要保证接口提供方和调用方的服务器时间为准确的网络同步时间)

4、把参数中除了_sign以外的参数进行加密,然后把加密结果和传过来的_sign比较,相同则执行调用请求。

5、如果服务器和客户端的时间没有同步,可以返回错误的同时候在返回一个服务器的当前时间,客户端接收到该错误后再请求上一个接口,时间则传服务器刚刚返回的时间

6、如果用户还没有登录时,还没有token之类的唯一标识时,可以和服务端定义一个固定的标识来使用就行。 7、涉及到比较重要的信息,可以用AES对value进行加密,防止抓包拉取到上传的数据。

8、追求安全可以考虑https的双向验证模式 + 参数的sign签名的规则双重验证达到安全的请求后台。

Java基础之三大特性

Java基础之三大特性

Java有三大特性继承、封装、多态

继承

  1. 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
  2. 子类拥有父类非private的属性和方法。
  3. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  4. 子类可以用自己的方式实现父类的方法。

阅读全文

计算机原理

计算机基础原理

现代计算机,大部分都是基于冯诺依曼体系结构,而我们这里谈论的也是此问前提。冯诺依曼的核心是:存储程序,顺序执行。所以不管计算机如何发展,基本原理是相同的。计算机程序实际上是告诉计算机做什么。

阅读全文

ARouter源码分析

ARouter的源码分析

分析需要储备以下知识点:

  • Java注解知识
  • Java反射知识
  • javapoet库(在注释里也有简单分析)
  • auto-service库

下面分析的时候会把各位看官当做已经掌握了以上知识点哈!

ARouter包含3个核心模块.jpg

ARouter包含3个核心模块

  1. arouter-annotation 注解的声明和信息存储类的模块
  2. arouter-compiler 编译期解析注解信息并生成相应类以便进行注入的模块
  3. arouter-api 核心调用Api功能的模块

阅读全文

ARouter简单入门和介绍

1.路由介绍

什么是路由框架?

说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能

为什么使用ARouter?

我们先从适用场景来分析:

  1. 动态跳转:一般来说复杂的电商跳转多页面需要很强的灵活性,很多情况下是运营人员动态配置的下发活动页面,需要灵活的进行跳转。

  2. 组件化:随着业务量的不断增长,app也会不断的膨胀,开发团队的规模和工作量也会逐渐增大,面对所衍生的64K问题、协作开发问题等,app一般都会走向组件化。组件化就是将APP按照一定的功能和业务拆分成多个组件module,不同的组件独立开发,组件化不仅能够提供团队的工作效率,还能够提高应用性能。而组件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系

  3. Native与H5的问题:现在的APP很少是纯Native的,也很少会有纯H5的,一般情况下都是将两者进行结合。这时候就需要非常便捷并且统一的跳转方案,因为在H5中是无法使用StartActivity()跳转到Native页面的,而从Native跳转到H5页面也只能通过配置浏览器的方式实现

  4. 其他等场景

阅读全文


Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2019 枫叶的博客 All Rights Reserved.

访客数 : | 访问量 :