JWT,oAuth和SSO的讨论
JWT,oAuth和SSO的讨论
背景
Single Sign On有很多成熟的方案。基于Session的服务常使用缓存Session信息在一个缓存服务上(例如redis)以实现SSO,每个微服务使用sessionId去缓存服务上取到对应的Session信息。
除此以外还有不基于Session的方案,类似于SAML和JWT。
SAML我不了解具体,这里讨论一下JWT。
oAuth和SSO
开始我把这俩搞混,以为是一个东西。实际上oAuth是一个标准,服务方用来给第三方认证用的,比如在王者荣耀里使用微信登陆,王者荣耀需要从微信获取用户的用户名、头像、性别等信息,使用微信登陆时,会跳转到微信的应用/页面中登陆,因此第三方并不知道微信的用户名密码。SSO是一种技术,可以允许用户登陆一次就可以访问其他服务,常用户多服务/微服务架构中,实现这种功能的技术有很多,而oAuth协议可以用来实现SSO(把多服务中的其他服务看做第三方)。
JWT
详细的JWT介绍参见这里
一个生成的JWT 如下构成:
Header.Payload.Signature
Header = base64UrlEncode(header)
header =
Payload = base64UrlEncode( 任何需要携带的非敏感数据 )
Signature = 加密算法( base64UrlEncode( header ) + "." + base64UrlEncode( payload ), 秘钥 )
可以这么说: 他们都是明文(仅仅经过base64编码),因此不应该把敏感数据放置到JWT的Payload中。一般只放一个UserName或UserId就可以。
如何使用JWT进行SSO
在用户第一次登录的时候利用类似JWTProducer.createToken
生成token写入cookie(或Http Authorization Header)。之后每次请求在一个javax.servlet.Filter中去验证token,类似JWTConsumer.verify
过程,而Payload的信息可以类似JWTConsumer.getContent
取出。
package tmp.JWT;
import java.io.UnsupportedEncodingException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
public class JWTProducer {
private Algorithm alg = null;
JWTProducer() {
try {
alg = Algorithm.HMAC256("secret");
} catch (IllegalArgumentException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String createToken() {
if (alg == null) {
return null;
}
// Put Claims here
return JWT.create().withClaim("user", "manager").withClaim("company", "SBODEMOUS").sign(alg);
}
public static void main(String[] args) {
JWTProducer pro = new JWTProducer();
System.out.println(pro.createToken());
}
}
package tmp.JWT;
import java.io.UnsupportedEncodingException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
public class JWTConsumer {
private Algorithm alg = null;
JWTVerifier verifier = null;
JWTConsumer() {
try {
alg = Algorithm.HMAC256("secret");
verifier = JWT.require(alg).build();
} catch (IllegalArgumentException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public DecodedJWT verify(String token) {
DecodedJWT jwt = verifier.verify(token);
return jwt;
}
public String getContent(String token) {
DecodedJWT jwt = this.verify(token);
return String.format("User:[%s]\tCompany:[%s]", jwt.getClaim("user").asString(),
jwt.getClaim("company").asString());
}
public static void main(String[] args) {
final String TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55IjoiU0JPREVNT1VTIiwidXNlciI6Im1hbmFnZXIifQ.6iXotJonkwK_7e8bmAw_3uIqwtFTx1tVxwIwhmIBhIg";
JWTConsumer con = new JWTConsumer();
System.out.println(con.getContent(TOKEN));
}
}
使用JWT注意事项
- HTTPS和http-only的cookie
- 强制验证HTTP Referer以防跨站点请求伪造
CSRF
CSRF,Cross Site Request Forgery, 跨站域请求伪造
用户访问A网站并登陆,Cookie还在时,就去访问B网站,而B网站可以利用A的Cookie去访问A的服务,从而对用户在A的权益造成损失。