背景描述
做app后端服务的coder都知道,很多服务都是无状态的,所谓的无状态,在这里我们可以简单的理解为(与传统web不同点)没有session。
那么这个时候怎么保证我们的请求安全性呢(这里我们所提到的安全性即用户请求来源和判断的安全性,不涉及验签,加密等数据来源之类的安全)。
需求描述
我们要实现一个基于token令牌的请求拦截系统,针对我们的每一次的请求进行拦截。以此来判断用户数据的完整性,安全性。
那么我们的拦截系统是什么样子的呢?
首先我们按照auth2.0的协议进行一个伪auth2.0设计一个权限分发验证的模式(因为我认为规则是人制订的,没必要所有的事都按照规则进行,方便即可)
那么我们的token鉴权是什么样的过程呢?
- 客户端登录->server
- 登录成功后颁发token令牌给客户端
- 客户端每次请求时候携带token
- server端针对需要鉴权的请求进行拦截
- server端对token进行鉴权
- 鉴权成功的话进行请求分发(既,继续请求),鉴权失败的话把请求拦截下来返回相应的失败状态码
关键点分析
整个过程中最重要的是3点:
- token的生成算法
- token的验证方式
- 针对需要鉴权的请求进行拦截
今天我们要做的就是第三点,如何实现针对需要鉴权的请求进行拦截
针对需要鉴权的请求进行拦截的实现过程
整个拦截的过程中主要有这么两个点:
- Server filters(服务器的过滤器)
- Dynamic binding(动态的过滤器拦截器绑定)
Server filters简述
Server filters主要是一个什么作用呢,这我们这里,他主要是负责两个地方:
- 拦截请求
- 业务内对token鉴权
整个这一块的代码如下
public class AuthorizationFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
//获取客户端Header中提交的token
String token = requestContext.getHeaderString("Authorization");
if (StringUtil.isEmpty(token)) {
// TODO:拦截响应
}
//判断该用户是否已登陆
User user = TokenUtils.sign(token);
if (user == null) {
// TODO:拦截响应
}
}
}
那么大家可能注意到我加了todo的地方,因为这些都是需要把请求拦截下来的地方,那么我们怎么实现吧这个请求拦截下来呢
我们采用了抛异常的方式,具体做法如下:
首先我们先声明一个auth验证失败的异常
public class AuthorizationException extends RuntimeException {
private String response = ErrorCode.NOT_AUTHED.getMsg();
public String getResponse(){
return this.response;
}
}
然后在全局捕获这个异常并输出提示信息
@Provider
public class AuthExceptionMapper implements ExceptionMapper<AuthorizationException> {
@Override
public Response toResponse(AuthorizationException exception) {
return Response.ok(exception.getResponse()).build();
}
}
Dynamic binding
在上面我们看到了如何对一个请求进行拦截,并进行鉴权验证,包括异常处理。
那么有个关键性的问题来了,我们该如何拦截需要鉴权的请求?
例如我登录的请求是不需要拦截的,获取个人信息的请求是需要拦截的
我怎么能做到上面的效果(答:Dynamic binding)
那么我们看一下什么是Dynamic binding?
让我们来看一下官方的解释
动态绑定就是一个以动态的方式分配资源方法的筛选器和拦截器。
那么我们怎么利用Dynamic binding来实现我们的区分拦截的功能呢
具体的需求描述
我想实现这么样的一个功能,通过一个注解,我只需要把这个注解标注在类或者方法上就能拦截这个对应的类或者方法
那么我怎么实现这个动态的过滤绑定功能呢?
首先,我们声明一个自定义的注解,命名为AuthAnnotation
然后我们亮出我们的实现办法
@Provider
public class AuthorizationFilterFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
List<Annotation> authzSpecs = new ArrayList<>();
Annotation classAuthzSpec =
resourceInfo.getResourceClass().getAnnotation(AuthAnnotation.class);
Annotation methodAuthzSpec =
resourceInfo.getResourceMethod().getAnnotation(AuthAnnotation.class);
if (classAuthzSpec != null)
authzSpecs.add(classAuthzSpec);
if (methodAuthzSpec != null)
authzSpecs.add(methodAuthzSpec);
if (!authzSpecs.isEmpty()) {
// 需要拦截的api
context.register(AuthorizationFilter.class);
}
}
}