Spring实现JWT简单实例,结合Spring Security。
JWT简介
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
Json web token(JWT)是为网络应用环境传递声明而执行的一种基于JSON的开发标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息。
JWT网络流程
- 浏览器发起登录请求,携带用户名和密码;
- 服务端验证身份,根据算法,将用户标识符打包生成 token;
- 服务器返回JWT信息给浏览器,JWT不包含敏感信息;
- 浏览器发起请求获取相关资源,携带 token数据发送到服务器;
- 服务器检测数据中的token,验明正身;
- 服务器允许该用户的资源请求;
- 该用户获取到后端返回的相关资源。
JWT数据结构
一个token分3部分,按顺序:头部(header)、载荷(payload)、签证(signature)。
token对象为一个很长的字符串,字符串之间通过”.”分隔符分为三个子串,各子串之间没有换行符,一般格式为:xxxxx.yyyyy.zzzzz 。
JWT的头部header承载两部分信息:声明类型,这里是jwt;声明加密的算法,通常直接使用 HMAC SHA256。
载荷payload 部分是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。 iss (issuer):签发人; exp (expiration time):过期时间; sub (subject):主题; aud (audience):受众; nbf (Not Before):生效时间; iat (Issued At):签发时间; jti (JWT ID):编号。
签证signature 部分是对前两部分的签名,防止数据篡改。需要指定一个密钥(secret)。然后,使用 Header 里指定的签名算法(默认是 HMAC SHA256)生成签名。
JWT优缺点
- JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。
- 当JWT未加密时,一些私密数据无法通过JWT传输。
- JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
- JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
- JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
- 为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。
引入依赖
1 | // JSON对象处理依赖 |
Security配置类实现:WebSecurityConfig
1 | import org.springframework.context.annotation.Configuration; |
用户身份认证信息实体类实现:AccountCredentials
1 | import lombok.AllArgsConstructor; |
Response返回类实现
1 | import com.alibaba.fastjson.JSONObject; |
登录拦截器实现:JWTLoginFilter
1 | import com.fasterxml.jackson.databind.ObjectMapper; |
权限或角色信息实体类实现:GrantedAuthorityImpl
1 | import lombok.Data; |
自定义验证类实现:CustomAuthenticationProvider
1 | import org.springframework.security.authentication.AuthenticationProvider; |
JWT生成及验证类实现:TokenAuthenticationService
1 | import com.alibaba.fastjson.JSONObject; |
其他请求验证类实现:JWTAuthenticationFilter
1 | import org.springframework.security.core.Authentication; |
hello测试类实现:TestController
1 | import org.springframework.web.bind.annotation.PostMapping; |
运行测试
登录
curl -H “Content-Type: application/json” -X POST -d ‘{“username”:”admin”,”password”:”111”}’ http://127.0.0.1:8080/login权限
curl -H “Content-Type: application/json” -H “Authorization: yegetaier xxx.yyy.zzz” http://127.0.0.1:8080/permission角色
curl -H “Content-Type: application/json” -H “Authorization: yegetaier xxx.yyy.zzz” http://127.0.0.1:8080/role其他
curl -H “Content-Type: application/json” -H “Authorization: yegetaier xxx.yyy.zzz” -X POST -d ‘{“index”:111}’ http://127.0.0.1:8080/hello
return: {“data”:”Hello, 111”,”message”:”success”,”status”:200}token:xxx.yyy.zzz
token前缀:yegetaier
token前缀与token拼接中,前缀与token之间的空格可要可不要。
Controller获取当前登录用户信息
登录成功后,认证信息载体Authentication的Principal字段存储了用户名信息。
实际场景中,Principal字段应该存储用户ID。
1 | import org.springframework.web.bind.annotation.*; |