1、概述
安全性在rest api开发中扮演着重要的角色。一个不安全的rest api可以直接访问到后台系统中的敏感数据。因此,企业组织需要关注api安全性。
spring security 提供了各种机制来保护我们的 rest api。其中之一是 api 密钥。api 密钥是客户端在调用 api 调用时提供的令牌。
在本教程中,我们将讨论如何在spring security中实现基于api密钥的身份验证。
2、rest api security
spring security可以用来保护rest api的安全性。rest api是无状态的,因此不应该使用会话或cookie。相反,应该使用basic authentication,api keys,jwt或oauth2-based tokens来确保其安全性。
2.1. basic authentication
basic authentication是一种简单的认证方案。客户端发送http请求,其中包含authorization标头的值为basic base64_url编码的用户名:密码。basic authentication仅在https / ssl等其他安全机制下才被认为是安全的。
2.2. oauth2
oauth2是rest api安全的行业标准。它是一种开放的认证和授权标准,允许资源所有者通过访问令牌将授权委托给客户端,以获得对私有数据的访问权限。
2.3. api keys
一些rest api使用api密钥进行身份验证。api密钥是一个标记,用于向api客户端标识api,而无需引用实际用户。标记可以作为查询字符串或在请求头中发送。
3、用api keys保护rest api
3.1 添加maven 依赖
让我们首先在我们的pom.xml中声明spring-boot-starter-security依赖关系:
org.springframework.boot spring-boot-starter-security 3.2 创建自定义过滤器(filter)
实现思路是从请求头中获取api key,然后使用我们的配置检查秘钥。在这种情况下,我们需要在spring security 配置类中添加一个自定义的filter。
我们将从实现genericfilterbean开始。genericfilterbean是一个基于javax.servlet.filter接口的简单spring实现。
让我们创建authenticationfilter类:
public class authenticationfilter extends genericfilterbean {
@override public void dofilter(servletrequest request, servletresponse response, filterchain filterchain) throws ioexception, servletexception { try { authentication authentication = authenticationservice.getauthentication((httpservletrequest) request); securitycontextholder.getcontext().setauthentication(authentication); } catch (exception exp) { httpservletresponse httpresponse = (httpservletresponse) response; httpresponse.setstatus(httpservletresponse.sc_unauthorized); httpresponse.setcontenttype(mediatype.application_json_value); printwriter writer = httpresponse.getwriter(); writer.print(exp.getmessage()); writer.flush(); writer.close(); } filterchain.dofilter(request, response); }} 我们只需要实现dofilter()方法,在这个方法中我们从请求头中获取api key,并将生成的authentication对象设置到当前的securitycontext实例中。
然后请求被传递给其余的过滤器处理,接着转发给dispatcherservlet最后到达我们的控制器。
在authenticationservice类中,实现从header中获取api key并构造authentication对象,代码如下:
public class authenticationservice {
private static final string auth_token_header_name = x-api-key; private static final string auth_token = baeldung; public static authentication getauthentication(httpservletrequest request) { string apikey = request.getheader(auth_token_header_name); if ((apikey == null) || !apikey.equals(auth_token)) { throw new badcredentialsexception(invalid api key); } return new apikeyauthentication(apikey, authorityutils.no_authorities); }} 在这里,我们检查请求头是否包含 api key,如果为空 或者key值不等于密钥,那么就抛出一个 badcredentialsexception。如果请求头包含 api key,并且验证通过,则将密钥添加到安全上下文中,然后调用下一个安全过滤器。getauthentication 方法非常简单,我们只是比较 api key 头部和密钥是否相等。
为了构建 authentication 对象,我们必须使用 spring security 为了标准身份验证而构建对象时使用的相同方法。所以,需要扩展 abstractauthenticationtoken 类并手动触发身份验证。
3.3. 扩展abstractauthenticationtoken
为了成功地实现我们应用的身份验证功能,我们需要将传入的api key转换为abstractauthenticationtoken类型的身份验证对象。abstractauthenticationtoken类实现了authentication接口,表示一个认证请求的主体和认证信息。
让我们创建apikeyauthentication类:
public class apikeyauthentication extends abstractauthenticationtoken {
private final string apikey; public apikeyauthentication(string apikey, collection authorities) { super(authorities); this.apikey = apikey; setauthenticated(true); } @override public object getcredentials() { return null; } @override public object getprincipal() { return apikey; }} apikeyauthentication 类是类型为 abstractauthenticationtoken 的对象,其中包含从 http 请求中获取的 apikey 信息。在构造方法中使用 setauthenticated(true) 方法。因此,authentication对象包含 apikey 和authenticated字段:
3.4. security config
通过创建建一个securityfilterchain bean,可以通过编程方式把我们上面编写的自定义过滤器(filter)进行注册。
我们需要在 httpsecurity 实例上使用 addfilterbefore() 方法在 usernamepasswordauthenticationfilter 类之前添加 authenticationfilter。
创建securityconfig 类:
@configuration@enablewebsecuritypublic class securityconfig { @bean public securityfilterchain filterchain(httpsecurity http) throws exception { http.csrf() .disable() .authorizerequests() .antmatchers(/**) .authenticated() .and() .httpbasic() .and() .sessionmanagement() .sessioncreationpolicy(sessioncreationpolicy.stateless) .and() .addfilterbefore(new authenticationfilter(), usernamepasswordauthenticationfilter.class); return http.build(); }} 此外注意代码中我们吧绘画策略(session policy)设置为无状态(stateless),因为我们使用的是rest。
3.5. resourcecontroller
最后,我们创建resourcecontroller,实现一个get请求 /home
@restcontrollerpublic class resourcecontroller { @getmapping(/home) public string homeendpoint() { return baeldung !; }} 3.6. 禁用 auto-configuration
@springbootapplication(exclude = {securityautoconfiguration.class, userdetailsserviceautoconfiguration.class})public class apikeysecretauthapplication { public static void main(string[] args) { springapplication.run(apikeysecretauthapplication.class, args); }} 4. 测试
我们先不提供api key进行测试
curl --location --request get 'http://localhost:8080/home'
返回 401 未经授权错误。
请求头中加上api key后,再次请求
curl --location --request get 'http://localhost:8080/home' --header 'x-api-key: baeldung' 请求返回状态200
如今智能手机已经可以站着进行无线充电
vivoNEX双屏版怎么样 足够惊艳同时体验上又没有短板
电力智能化运维平台:提高效率和保障电力系统的稳定运行
2021年的5G领域将发生什么变化?
华为P10被三星S8吊打,但我坐等小米6的到来
如何在Spring Security中实现基于API密钥的身份验证
基于FPGA的DDR3 SDRAM控制器用户接口设计
为边缘的AI定义适当的计算能力
英飞凌的MCU产品线将导入EtherCAT 以满足工业自动化市场的通讯需求
明文存储的Signal Desktop 应用程序消息解密密钥
2023年第四季度国内手机市场:苹果强势领先,华为业绩猛增
Google:可迁移架构探索,用AI设计AI芯片
DSP+ARM+FPGA开发板 板载 双网口/2路RS485/2路RS232/ADC/DAC/CAN
博阳能源:重负载下的高性价比是行业首选
什么是励磁电流? 什么是变压器励磁涌流?
华为新款智能眼镜发布,搭载Harmony OS的智能眼镜会有什么不同
浅析机器学习的前沿技术及技术趋势
智能传感“关键”蚌埠造
稳压器为什么总是跳闸 稳压器经常跳闸怎么解决
现阶段对尾气的处理方法