java 单机接口限流处理方案
268
2022-11-04
SpringBoot采用jwt作为REST API安全机制的应用示例
对外提供数据接口,安全性是必须考虑的问题。JWT无需存储客户端状态,也无须预先分配api_key、secrity_key,仅需校验数据签名,检查时间戳,就能保证请求的身份信息的完整性和防止重放攻击;而对于客户端来说,也没有跨域问题,不失为一种轻量的REST API安全机制。
一、概述
WebService有两种方案:基于SOAP的RPC和基于HTTP的REST API。REST API轻便,无状态,伸缩性更好,得到广泛使用,但缺少对安全性的直接支持,需要自己解决安全性问题。
安全性设计是个宏大的命题。这里说的REST API的安全性,只包括身份验证和授权策略2个方面,再有就是数据传输。数据传输现在一般都采用API来说,主要工作在服务器端,不会有什么大问题。所以REST API的安全性设计主要是身份验证。
REST API的身份验证方案,大约有这么几种:HTTP Basic、HTTP Digest、API KEY、Oauth 和 JWT。
1、HTTP Basic 每次请求,简单的将用户名和密码 base64 编码放到header中,传送给服务器。所以安全性较低。一定要配合ssl进行数据传输,否则无异于裸奔。
2.API KEY Client 端向服务端注册,获得api_key以及security_key。请求的时候,客户端根据 api_key、secrity_key、timestrap、rest_uri 采用 hmacsha256 算法得到一个 hash 值 sign,发送给服务端。
服务端收到该请求后,首先验证 api_key 和 security_key,接着验证 timestrap 是否超过时间限制,最后计算 sign 值,和传过来的sign 值做校验。这样的设计就防止了数据被篡改和重放攻击。
3.Oauth1.0a 或者 Oauth2 OAuth 协议适用于为外部应用授权访问本站资源的情况。可见拙作:oAuth
4.JWT JWT(JSON Web Token)。第一次身份认证后,服务器生成一个token给客户端,客户端之后每次请求,都带上这个token。听上去与第一点的basic类似,但这个token有时间戳和签名,同样可以保证token的完整性和防止重放攻击。
不过,token里的信息,除了签名,其余部分也只是使用base64简单处理,跟明文无异,靠签名校验来保证安全性,因此也应该使用SSL进行数据传输,同时有效时间也不宜设置过久,30分钟足矣。(当然,原始token产生以后,我们自行进行加密也是可以的)
二、JWT的工作原理
1、工作过程
2、JWT的数据结构
大约类似这样:
它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。
JWT 的三个部分依次如下。
Header(头部) Payload(负载) Signature(签名)
写成一行,就是下面的样子
Header.Payload.Signature
3、Signature Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
三、示例
1、服务器端代码结构(JAVA)
2、代码概述
控制器与外部交互,提供接口给客户端,包括身份认证接口、数据获取接口等;
身份认证时,系统验证客户端提交的用户名和密码,通过后生成JWT返回客户端;
客户端请求数据时,将JWT附在header中。拦截器会检查JWT,有效则返回数据。
3、代码明细1)TokenUser.java
public class TokenUser { private String name; private String password; private String ip; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; }}
2)JWT静态类及相关pom.xml
import com.auth0.jwt.JWT;import com.auth0.jwt.JWTCreator;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;import java.util.Map;public class JwtUtils { private static String SECRET = "略去"; //密钥 /* 为了保证令牌的安全性,jwt令牌由三个部分组成,分别是: header:令牌头部,记录了整个令牌的类型和签名算法 payload:令牌负荷,记录了保存的主体信息,比如你要保存的用户信息就可以放到这里 signature:令牌签名,按照头部固定的签名算法对整个令牌进行签名,该签名的作用是:保证令牌不被伪造和篡改 它们组合而成的完整格式是:header.payload.signature */ /** * 生成token * * @param map //传入payload * payload:令牌负荷,记录了保存的主体信息,比如你要保存的用户信息就可以放到这里 * @return 返回token */ public static String getToken(Map
pom.xml中要引入jwt类库
3)拦截器及拦截器使能
(1)拦截器
mport com.auth0.jwt.exceptions.AlgorithmMismatchException;import com.auth0.jwt.exceptions.SignatureVerificationException;import com.auth0.jwt.exceptions.TokenExpiredException;import com.fasterxml.jackson.databind.ObjectMapper;import com.gzdd.rainapi.utils.JwtUtils;import org.springframework.org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.javax.servlet.java.util.HashMap;import java.util.Map;/** * JWT验证拦截器 */@Componentpublic class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /** * 前后端分离有时候会有两次请求,第一次为OPTIONS请求,默认会拦截所有请求,但是第一次请求又获取不到jwt,所以会出错。 **/ if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { return true; } Map
(2)使用拦截器
import com.gzdd.rainapi.interceptor.JwtInterceptor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class SecurityConfiguration implements WebMvcConfigurer { @Autowired private JwtInterceptor jwtI; public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtI) .addPathPatterns("/data/*");//取数据的接口,必须检查JWT }}
4)控制器 (1)身份认证
import com.gzdd.rainapi.config.AppConfig;import com.gzdd.rainapi.pojo.TokenUser;import com.gzdd.rainapi.utils.IPUtils;import com.gzdd.rainapi.utils.JwtUtils;import com.gzdd.rainapi.utils.Result;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.java.util.HashMap;import java.util.Map;@RestControllerpublic class IndexController { @Autowired AppConfig appConfig; @RequestMapping(value = {"/test"}) public String noCheck(Model model) { return "Hello world!"; } //登录时生成token @PostMapping(value = "/token") public Result getToken(HttpServletRequest request, @RequestBody TokenUser user) { if (!appConfig.isValidUser(user.getName(), user.getPassword())) { return Result.error("非法的用户"); } Map
(2)数据接口
import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;//注意这些接口以/data/开头,符合拦截器使能条件@RestController@RequestMapping(value = "/data")public class DataController { @RequestMapping(value = {"/test"}) public String test(Model model) { return "welcome to data api!"; }}
5)测试结果
(1)身份认证
(2)数据获取
参考文章:REST API 安全设计指南JSON Web Token 入门教程
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~