- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本文通过一个简易安全认证示例的开发实践,理解过滤器和拦截器的工作原理.
很多文章都将过滤器(filter)、拦截器(interceptor)和监听器(listener)这三者和spring关联起来讲解,并认为过滤器(filter)、拦截器(interceptor)和监听器(listener)是spring提供的应用广泛的组件功能.
但是严格来说,过滤器和监听器属于servlet范畴的api,和spring没什么关系.
因为过滤器继承自javax.servlet.filter接口,监听器继承自javax.servlet.servletcontextlistener接口,只有拦截器继承的是org.springframework.web.servlet.handlerinterceptor接口.
上面的流程图参考自网上资料,一图胜千言。看完本文以后,将对过滤器和拦截器的调用过程会有更深刻理解.
1、安全认证设计思路 。
有时候内外网调用api,对安全性的要求不一样,很多情况下外网调用api的种种限制在内网根本没有必要,但是网关部署的时候,可能因为成本和复杂度等问题,内外网要调用的api会部署在一起.
实现rest接口的安全性,可以通过成熟框架如spring security或者 shiro 搞定.
但是因为安全框架往往实现复杂(我数了下spring security,洋洋洒洒大概有11个核心模块,shiro的源码代码量也比较惊人)同时可能要引入复杂配置(能不能让人痛快一点),不利于中小团队的灵活快速开发、部署及问题排查.
很多团队自己造轮子实现安全认证,本文这个简易认证示例参考自我所在的前厂开发团队,可以认为是个基于token的安全认证服务.
大致设计思路如下:
1、自定义http请求头,每次调用api都在请求头里传人一个token值 。
2、token放在缓存(如redis)中,根据业务和api的不同设置不同策略的过期时间 。
3、token可以设置白名单和黑名单,可以限制api调用频率,便于开发和测试,便于紧急处理异状,甚至临时关闭api 。
4、外网调用必须传人token,token可以和用户有关系,比如每次打开页面或者登录生成token写入请求头,页面验证cookie和token有效性等 。
在spring security框架里有两个概念,即 认证 和 授权 ,认证指可以访问系统的用户,而授权则是用户可以访问的资源.
实现上述简易安全认证需求,你可能需要独立出一个token服务,保证生成token全局唯一,可能包含的模块有自定义流水生成器、crm、加解密、日志、api统计、缓存等,但是和用户(crm)其实是弱绑定关系。某些和用户有关系的公共服务,比如我们经常用到的发送短信sms和邮件服务,也可以通过token机制解决安全调用问题.
综上,本文的简易安全认证其实和spring security框架提供的认证和授权有点不一样,当然,这种“安全”处理方式对专业人士没什么新意,但是可以对外挡掉很大一部分小白用户.
2、自定义filter 。
和spring mvc类似,spring boot提供了很多servlet过滤器(filter)可使用,并且它自动添加了一些常用过滤器,比如characterencodingfilter(用于处理编码问题)、hiddenhttpmethodfilter(隐藏http函数)、httpputformcontentfilter(form表单处理)、requestcontextfilter(请求上下文)等。通常我们还会自定义filter实现一些通用功能,比如记录日志、判断是否登录、权限验证等.
1、自定义请求头 。
很简单,在request header添加自定义请求头authtoken:
1
2
3
4
5
6
7
8
9
10
|
@requestmapping
(value =
"/getinfobyid"
, method = requestmethod.post)
@apioperation
(
"根据商品id查询商品信息"
)
@apiimplicitparams
({
@apiimplicitparam
(paramtype =
"header"
, name =
"authtoken"
, required =
true
, value =
"authtoken"
, datatype =
"string"
),
})
public
getgoodsbygoodsidresponse getgoodsbygoodsid(
@requestheader
string authtoken,
@requestbody
getgoodsbygoodsidrequest request) {
return
_goodsapiservice.getgoodsbygoodsid(request);
}
getgoodsbygoodsid
|
加了@requestheader修饰的authtoken字段就可以在swagger这样的框架下显示出来.
调用后,可以根据http工具看到请求头,本文示例是authtoken(和某些框架的token区分开):
备注:很多httpclient工具都支持动态传人请求头,比如resttemplate.
2、实现filter 。
filter接口共有三个方法,即init,dofilter和destory,看到名称就大概知道它们主要用途了,通常我们只要在dofilter这个方法内,对http请求进行处理:
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
|
package
com.power.demo.controller.filter;
import
com.power.demo.common.appconst;
import
com.power.demo.common.bizresult;
import
com.power.demo.service.contract.authtokenservice;
import
com.power.demo.util.powerlogger;
import
com.power.demo.util.serializeutil;
import
org.springframework.beans.factory.annotation.autowired;
import
org.springframework.stereotype.component;
import
javax.servlet.*;
import
javax.servlet.http.httpservletrequest;
import
java.io.ioexception;
@component
public
class
authtokenfilter
implements
filter {
@autowired
private
authtokenservice authtokenservice;
@override
public
void
init(filterconfig var1)
throws
servletexception {
}
@override
public
void
dofilter(servletrequest request, servletresponse response, filterchain chain)
throws
ioexception, servletexception {
httpservletrequest req = (httpservletrequest) request;
string token = req.getheader(appconst.auth_token);
bizresult<string> bizresult = authtokenservice.powercheck(token);
system.out.println(serializeutil.serialize(bizresult));
if
(bizresult.getisok() ==
true
) {
powerlogger.info(
"auth token filter passed"
);
chain.dofilter(request, response);
}
else
{
throw
new
servletexception(bizresult.getmessage());
}
}
@override
public
void
destroy() {
}
}
authtokenfilter
|
注意,filter这样的东西,我认为从实际分层角度,多数处理的还是表现层偏多,不建议直接在filter中直接使用数据访问层dao,虽然这样的代码一两年前我在很多老古董项目中看到过很多次,而且<<spring实战>>的书里也有这样写的先例.
3、认证服务 。
这里就是主要业务逻辑了,示例代码只是简单写下思路,不要轻易就用于生产环境:
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
|
package
com.power.demo.service.impl;
import
com.power.demo.cache.powercachebuilder;
import
com.power.demo.common.bizresult;
import
com.power.demo.service.contract.authtokenservice;
import
org.springframework.beans.factory.annotation.autowired;
import
org.springframework.stereotype.component;
import
org.springframework.util.stringutils;
@component
public
class
authtokenserviceimpl
implements
authtokenservice {
@autowired
private
powercachebuilder cachebuilder;
/*
* 验证请求头token是否合法
* */
@override
public
bizresult<string> powercheck(string token) {
bizresult<string> bizresult =
new
bizresult<>(
true
,
"验证通过"
);
system.out.println(
"token的值为:"
+ token);
if
(stringutils.isempty(token) ==
true
) {
bizresult.setfail(
"authtoken为空"
);
return
bizresult;
}
//处理黑名单
bizresult = checkforbidlist(token);
if
(bizresult.getisok() ==
false
) {
return
bizresult;
}
//处理白名单
bizresult = checkallowlist(token);
if
(bizresult.getisok() ==
false
) {
return
bizresult;
}
string key = string.format(
"power.authtokenservice.%s"
, token);
//cachebuilder.set(key, token);
//cachebuilder.set(key, token.touppercase());
//从缓存中取
string existtoken = cachebuilder.get(key);
if
(stringutils.isempty(existtoken) ==
true
) {
bizresult.setfail(string.format(
"不存在此authtoken:%s"
, token));
return
bizresult;
}
//比较token是否相同
boolean
isequal = token.equals(existtoken);
if
(isequal ==
false
) {
bizresult.setfail(string.format(
"不合法的authtoken:%s"
, token));
return
bizresult;
}
//do something
return
bizresult;
}
}
authtokenserviceimpl
|
用到的缓存服务可以参考这里,这个也是我在前厂的经验总结.
4、注册filter 。
常见的有两种写法:
(1)、使用@webfilter注解来标识filter 。
1
2
3
|
@order
(
1
)
@webfilter
(urlpatterns = {
"/api/v1/goods/*"
,
"/api/v1/userinfo/*"
})
public
class
authtokenfilter
implements
filter {
|
使用@webfilter注解,还可以配合使用@order注解,@order注解表示执行过滤顺序,值越小,越先执行,这个order大小在我们编程过程中就像处理http请求的生命周期一样大有用处。当然,如果没有指定order,则过滤器的调用顺序跟添加的过滤器顺序相反,过滤器的实现是责任链模式.
最后,在启动类上添加@servletcomponentscan 注解即可正常使用自定义过滤器了.
(2)、使用filterregistrationbean对filter进行自定义注册 。
本文以第二种实现自定义filter注册:
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
|
package
com.power.demo.controller.filter;
import
com.google.common.collect.lists;
import
org.springframework.beans.factory.annotation.autowired;
import
org.springframework.boot.web.servlet.filterregistrationbean;
import
org.springframework.context.annotation.bean;
import
org.springframework.context.annotation.configuration;
import
org.springframework.stereotype.component;
import
java.util.list;
@configuration
@component
public
class
restfilterconfig {
@autowired
private
authtokenfilter filter;
@bean
public
filterregistrationbean filterregistrationbean() {
filterregistrationbean registrationbean =
new
filterregistrationbean();
registrationbean.setfilter(filter);
//设置(模糊)匹配的url
list<string> urlpatterns = lists.newarraylist();
urlpatterns.add(
"/api/v1/goods/*"
);
urlpatterns.add(
"/api/v1/userinfo/*"
);
registrationbean.seturlpatterns(urlpatterns);
registrationbean.setorder(
1
);
registrationbean.setenabled(
true
);
return
registrationbean;
}
}
restfilterconfig
|
请大家特别注意urlpatterns,属性urlpatterns指定要过滤的url模式。对于filter的作用区域,这个参数居功至伟.
注册好filter,当spring boot启动时监测到有javax.servlet.filter的bean时就会自动加入过滤器调用链applicationfilterchain.
调用一个api试试效果:
通常情况下,我们在spring boot下都会自定义一个全局统一的异常管理增强 globalexceptionhandler (和上面这个显示会略有不同).
根据我的实践,过滤器里抛出异常,不会被全局唯一的异常管理增强捕获到并进行处理,这个和拦截器inteceptor以及下一篇文章介绍的自定义aop拦截不同.
到这里,一个通过自定义filter实现的简易安全认证服务就搞定了.
3、自定义拦截器 。
1、实现拦截器 。
继承接口handlerinterceptor,实现拦截器,接口方法有下面三个:
prehandle是请求执行前执行 。
posthandle是请求结束执行 。
aftercompletion是视图渲染完成后执行 。
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
|
package
com.power.demo.controller.interceptor;
import
com.power.demo.common.appconst;
import
com.power.demo.common.bizresult;
import
com.power.demo.service.contract.authtokenservice;
import
com.power.demo.util.powerlogger;
import
com.power.demo.util.serializeutil;
import
org.springframework.beans.factory.annotation.autowired;
import
org.springframework.stereotype.component;
import
org.springframework.web.servlet.handlerinterceptor;
import
org.springframework.web.servlet.modelandview;
import
javax.servlet.http.httpservletrequest;
import
javax.servlet.http.httpservletresponse;
/*
* 认证token拦截器
* */
@component
public class authtokeninterceptor implements handlerinterceptor {
@autowired
private authtokenservice authtokenservice;
/*
* 请求执行前执行
* */
@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
boolean handleresult = false;
string token = request.getheader(appconst.auth_token);
bizresult<string> bizresult = authtokenservice.powercheck(token);
system.out.println(serializeutil.serialize(bizresult));
handleresult = bizresult.getisok();
powerlogger.info("auth token interceptor拦截结果:" + handleresult);
if (bizresult.getisok() == true) {
powerlogger.info("auth token interceptor passed");
} else {
throw new exception(bizresult.getmessage());
}
return handleresult;
}
/*
* 请求结束执行
* */
@override
public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
}
/*
* 视图渲染完成后执行
* */
@override
public
void
aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex)
throws
exception {
}
}
authtokeninterceptor
|
示例中,我们选择在请求执行前进行token安全认证.
认证服务就是过滤器里介绍的authtokenservice,业务逻辑层实现复用.
2、注册拦截器 。
定义一个interceptorconfig类,继承自webmvcconfigurationsupport,webmvcconfigureradapter已经过时.
将authtokeninterceptor作为bean注入,其他设置拦截器拦截的url和过滤器非常相似:
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
|
package
com.power.demo.controller.interceptor;
import
com.google.common.collect.lists;
import
org.springframework.context.annotation.bean;
import
org.springframework.context.annotation.configuration;
import
org.springframework.stereotype.component;
import
org.springframework.web.servlet.config.annotation.defaultservlethandlerconfigurer;
import
org.springframework.web.servlet.config.annotation.interceptorregistry;
import
org.springframework.web.servlet.config.annotation.resourcehandlerregistry;
import
org.springframework.web.servlet.config.annotation.webmvcconfigurationsupport;
import
java.util.list;
@configuration
@component
public
class
interceptorconfig
extends
webmvcconfigurationsupport {
//webmvcconfigureradapter已经过时
private
static
final
string favicon_url =
"/favicon.ico"
;
/**
* 发现如果继承了webmvcconfigurationsupport,则在yml中配置的相关内容会失效。
*
* @param registry
*/
@override
public
void
addresourcehandlers(resourcehandlerregistry registry) {
registry.addresourcehandler(
"/"
).addresourcelocations(
"/**"
);
registry.addresourcehandler(
"/static/**"
).addresourcelocations(
"classpath:/static/"
);
}
/**
* 配置servlet处理
*/
@override
public
void
configuredefaultservlethandling(defaultservlethandlerconfigurer configurer) {
configurer.enable();
}
@override
public
void
addinterceptors(interceptorregistry registry) {
//设置(模糊)匹配的url
list<string> urlpatterns = lists.newarraylist();
urlpatterns.add(
"/api/v1/goods/*"
);
urlpatterns.add(
"/api/v1/userinfo/*"
);
registry.addinterceptor(authtokeninterceptor()).addpathpatterns(urlpatterns).excludepathpatterns(favicon_url);
super
.addinterceptors(registry);
}
//将拦截器作为bean写入配置中
@bean
public
authtokeninterceptor authtokeninterceptor() {
return
new
authtokeninterceptor();
}
}
interceptorconfig
|
启动应用后,调用接口就可以看到拦截器拦截的效果了。全局统一的异常管理 globalexceptionhandler 捕获异常后处理如下:
和过滤器显示的主要错误提示信息几乎一样,但是堆栈信息更加丰富.
4、过滤器和拦截器区别 。
主要区别如下:
1、拦截器主要是基于java的反射机制的,而过滤器是基于函数回调 。
2、拦截器不依赖于servlet容器,过滤器依赖于servlet容器 。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用 。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问 。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次 。
参考过的一些文章,有的说“拦截器可以获取ioc容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑”,经过实际验证,这是不对的.
注意:过滤器的触发时机是容器后,servlet之前,所以过滤器的 dofilter (servletrequest request, servletresponse response, filterchain chain)的入参是servletrequest,而不是httpservletrequest,因为过滤器是在httpservlet之前。下面这个图,可以让你对filter和interceptor的执行时机有更加直观的认识:
只有经过dispatcherservlet 的请求,才会走拦截器链,自定义的servlet请求是不会被拦截的,比如我们自定义的servlet地址http://localhost:9090/testservlet是不会被拦截器拦截的。但不管是属于哪个servlet,只要符合过滤器的过滤规则,过滤器都会执行.
根据上述分析,理解原理,实际操作就简单了,哪怕是asp.net过滤器亦然.
问题:实现更加灵活的安全认证 。
在java web下通过自定义过滤器filter或者拦截器interceptor通过urlpatterns,可以实现对特定匹配的api进行安全认证,比如匹配所有api、匹配某个或某几个api等,但是有时候这种匹配模式对开发人员相对不够友好.
我们可以参考spring security那样,通过注解+spel实现强大功能.
又比如在asp.net中,我们经常用到authorized特性,这个特性可以加在类上,也可以作用于方法上,可以更加动态灵活地控制安全认证.
我们没有选择spring security,那就自己实现类似authorized的灵活的安全认证,主要实现技术就是我们所熟知的aop.
通过aop方式实现更灵活的拦截的基础知识本文就先不提了,更多的关于aop的话题将在下篇文章分享.
总结 。
以上所述是小编给大家介绍的spring boot使用过滤器和拦截器分别实现rest接口简易安全认证,希望对大家有所帮助如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我网站的支持! 。
原文链接:http://www.cnblogs.com/jeffwongishandsome/p/spring-boot-use-filter-and-interceptor-to-implement-an-easy-auth-system.html 。
最后此篇关于Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解的文章就讲到这里了,如果你想了解更多关于Spring Boot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我对编程真的很陌生,并且在理解 RESTful API 的概念时遇到了一些麻烦。我读过 REST 和 RESTful API。我已经查看了 SO 中已经提出的问题,但似乎无法更好地理解该主题。 在我的
我以为我知道REST /“RESTFul”,restfulservices,webservices,SOA和微服务是什么,但是我遇到了许多不同的定义,我得出的结论是这些术语被过度使用,滥用或完全错误定
我有一个列表,其中有一个“人员和组”列。当我使用 REST 查询行时,我会在此列中列出用户 ID。 我发现这篇文章将帮助我将每个 id 转换为标题 http://www.codeproject.com
我想问一些关于 REST 调用的问题。我是 REST 调用的绿色,我想了解什么是 REST 调用以及如何使用 URL 向服务器发送 REST 调用。谁能给我一些基本的教程或链接供我引用? 另外,如果我
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 8年前关闭
如果有一个 REST 资源我想监视来自其他客户端的更改或修改,那么最好(也是最 RESTful)的方法是什么? 我这样做的一个想法是通过提供特定资源来保持连接打开,而不是在资源不(尚)存在时立即返回。
我有一个可以返回大量项目的 RESTful API,我希望能够使用分页样式技术来限制项目数量,这是 RESTful API 中的一个好主意吗? 如果有可能最好通过链接(在这种情况下为 url)或请求正
我仍然处于适应以 REST 方式做事的过程中。 在我的情况下,客户端软件将与 RESTful 服务交互。很少,客户端会上传其整个实体数据库(每个实体序列化为大约 5kb 的 xml 块)。 也许我错了
设计一个路径解析可能有歧义的 REST API 是否被认为是不好的做法?例如: GET /animals/{id} // Returns the animal with the given ID
我知道 REST 并且知道在不使用 session 的情况下创建 RESTful Web 服务,我更了解它,但我不太了解无状态的概念以及使用 REST 如何使您的应用程序可扩展 有人可以解释 REST
我正在尝试找到解决以下问题的最佳方法:我们的应用程序是SaaS,它支持Web登录的SAML。该应用程序还公开了应该在自动化和无人值守的流程中使用的REST API,这意味着没有交互式用户可以键入凭据。
由于 REST 是无状态的,因此传入的每个请求都不知道传入的前一个请求。在这种情况下是否可以使用连接池? 如果要实现连接池,它将像标准数据库连接一样在每个请求时打开连接池并关闭它。 如何实现 REST
得墨忒耳定律(真的应该是得墨忒耳的建议)说你不应该“穿过”一个物体去接触它们的子物体。如果您作为客户需要执行一些重要的操作,大多数情况下您使用的域模型应该支持该操作。 REST 原则上是一个愚蠢的对象
我唯一真正接触到 REST 的想法已经通过 Ruby on Rails 的 RESTful routing .这非常适合我使用 Rails 构建的基于 CRUD 的应用程序,但因此我对 RESTful
有什么好处 http://www.example.com/app/servlet/cat1/cat2/item 网址 超过 http://www.example.com/app/servlet?c
我知道以前有人问过这类问题。我有我的问题的解决方案,我想知道我是否在任何地方破坏了 REST 或 HTTP 主体。 在我的系统中,我有一个名为 member 的资源。支持通常的GET/POST/PUT
我有一个API,可以执行一些批量处理任务。假设它确实为某些资源命名。 我批量传递了7个请求,其中5个更新成功,2个失败。 我的问题是如何应对。使用HTTP时,我无法同时返回成功和错误。 有一个部分成功
我来自 RPC 世界,但目前正在调查使用 REST 是否适合我的项目。至于据我了解 Wikipedia RESTful 服务的基本思想是提供对集合及其各个元素的访问。 在我的情况下,服务器将是一个测量
我想将REST添加到我的挂毯项目中,因此需要知道如何实现它。 有什么更好的方法? 谢谢。 [编辑,从答案中复制:]我必须将GET,PUT,POST和DELETE服务添加到我的挂毯应用程序中。我看到Ta
让 /users/{id}成为 RESTful 服务中的资源 url。 启用基本身份验证,只有经过身份验证的用户才能访问该 url。 示例场景: User_1 & User_2是经过身份验证的用户,用
我是一名优秀的程序员,十分优秀!