b2b2c系統jwt權限源碼分享part1

需求分析

在分享源碼之前,先將b2b2c系統中權限模塊的需求整理、明確,方便源碼的理解。

業務需求

b2b2c電子商務系統中權限主要有三個角色:買家、賣家、平臺管理員。其中賣家角色中又有店員,可以設置店員管理不同的權限(如商品和訂單的權限分派給不同的店員),同理平臺管理員也需要進行上述精細權限的管理,買家權限相對比較單一。如果禁用了某個店員或管理員,則這個用戶需要立刻被登出,保證數據安全性

技術需求

去中心化

javashop電商系統採用去中心化、容器化的部署方案,考慮性能及擴展性,鑑權需要採用token的方式,不能採用有中心的session方案

公用能力抽象

b2b2c電商體系中存在三端(買家、賣家、管理端),出於性能、穩定性考慮,這三端在部署上是分離的,體現為買家API、賣家API、管理端API,權限本質上就是攔截這三端的api請求,進行鑑權,這三種角色的鑑權既有通用的邏輯又有個性化的邏輯:

通用:token的生成和解析個性化:權限數據源不同(SecurityMetadataSource)

具體體現就是角色和權限綁定關係的來源不同:賣家端來自賣家的權限設置,平臺的來自管理端的權限設置。

這就要求在架構和代碼實現上做的該重用的重用,該分離的分離。

架構思路

Token解析架構思路

兩個接口分別對應token的解析和token的生成默認實現了一個jwt的實現類

安全認證領域模型架構

AuthUser是最上層的可被認證用戶接口User為基礎實現Buyer,Seller,Admin為具體業務實現

基於JWT的權限認證源碼

TokenManager

Token的業務類接口,有兩個核心的方法:創建和解析token,擴展性的考慮,接口層面並未體現jwt的依賴:

<code>/**
* token業務管理接口
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019/12/25
*/
public interface TokenManager {

/**
* 創建token
* @param user
* @return


*/
Token create(AuthUser user);

/**
* 解析token
* @param token
* @return 用戶對象
*/
T parse(Class clz, String token) throws TokenParseException;
}/<code>

TokenManagerImpl

token業務類基於jwt的實現:

<code>/**
* token管理基於twt的實現
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019/12/25
*/

@Service
public class TokenManagerImpl implements TokenManager {

@Autowired
private JavashopConfig javashopConfig;

@Override
public Token create(AuthUser user) {
JwtTokenCreater tokenCreater = new JwtTokenCreater(javashopConfig.getTokenSecret());
tokenCreater.setAccessTokenExp(javashopConfig.getAccessTokenTimeout());
tokenCreater.setRefreshTokenExp(javashopConfig.getRefreshTokenTimeout());
return tokenCreater.create(user);

}

@Override
public T parse(Class clz, String token) throws TokenParseException {
JwtTokenParser tokenParser = new JwtTokenParser(javashopConfig.getTokenSecret());
return tokenParser.parse(clz, token);
}
}/<code>

Token創建接口

<code>/**
* Token創建接口
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019-06-21
*/
public interface TokenCreater {


/**
* 創建token
* @param user 用戶
* @return token
*/
Token create(AuthUser user);

}/<code>

Token 解析器

<code>/**
* Token 解析器
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019-06-21
*/
public interface TokenParser {

/**
* 解析token
* @param token
* @return 用戶對象
*/
T parse(Class clz, String token) throws TokenParseException;

}/<code>

JwtTokenCreater

基於jwt token的創建實現:

<code>/**
* Jwt token 創建實現


*
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019-06-21
*/

public class JwtTokenCreater implements TokenCreater {

/**
* jwt秘鑰,需要在構造器中初始化
*/
private String secret;

/**
* 訪問token的有效期,在構造器中初始化,可以通過setter改變
*/
private int accessTokenExp;

/**
* 刷新token的有效期,在構造器中初始化,可以通過setter改變
*/
private int refreshTokenExp;

/**
* 在構造器中初始化參數、默認值
* @param secret
*/
public JwtTokenCreater(String secret) {

this.secret = secret;

accessTokenExp=60*60;

//默認session失效時間為1小時:60秒 x 60 (=1分鐘) * 60 (=1小時)
refreshTokenExp = 60 * 60 * 60;
}

@Override
public Token create(AuthUser user) {

ObjectMapper oMapper = new ObjectMapper();


Map buyerMap = oMapper.convertValue(user, HashMap.class);

String accessToken = Jwts.builder()
.setClaims(buyerMap)
.setSubject("user")
.setExpiration( new Date(System.currentTimeMillis() + accessTokenExp * 1000))
.signWith(SignatureAlgorithm.HS512, secret.getBytes())
.compact();

String refreshToken = Jwts.builder()
.setClaims(buyerMap)
.setSubject("user")
.setExpiration( new Date(System.currentTimeMillis() +(accessTokenExp+ refreshTokenExp) * 1000))
.signWith(SignatureAlgorithm.HS512, secret.getBytes())
.compact();

Token token = new Token();
token.setAccessToken(accessToken);
token.setRefreshToken(refreshToken);


return token;
}


public JwtTokenCreater setSecret(String secret) {
this.secret = secret;
return this;
}

public JwtTokenCreater setAccessTokenExp(int accessTokenExp) {
this.accessTokenExp = accessTokenExp;
return this;
}

public JwtTokenCreater setRefreshTokenExp(int refreshTokenExp) {
this.refreshTokenExp = refreshTokenExp;
return this;
}/<code>

JwtTokenParser

基於jwt的token解析器

<code>/**

* jwt token解析器
* @author kingapex


* @version 1.0
* @since 7.1.0
* 2019-06-24
*/

public class JwtTokenParser implements TokenParser {

/**
* jwt秘鑰,需要在構造器中初始化
*/
private String secret;

private Claims claims;

public JwtTokenParser(String secret) {
this.secret = secret;
}


@Override
public T parse(Class clz, String token) throws TokenParseException {

try {
claims
= Jwts.parser()
.setSigningKey(secret.getBytes())
.parseClaimsJws(token).getBody();
T t = BeanUtil.mapToBean(clz, claims);
return t;
} catch (Exception e) {
throw new TokenParseException(e);
}

}/<code>

AuthUser

認證用戶接口

<code>/**
* 認證用戶接口
* @author kingapex
* @version 1.0
* @since 7.1.0
* 2019-06-21
*/
public interface AuthUser {



List<string> getRoles();

void setRoles(List<string> roles);
}/<string>/<string>/<code>

基於上述接口實現三種角色 :Buyer,Seller,Admin

User:

基類

<code>/**
* 用戶
* Created by kingapex on 2018/3/8.
*
* @author kingapex
* @version 1.0
* @since 6.4.0
* 2018/3/8
*/
public class User implements AuthUser {

/**
* 會員id
*/
private Integer uid;

/**
* 唯一標識
*/
private String uuid;

/**
* 用戶名
*/
private String username;
/**
* 角色
*/
private List<string> roles;

public User() {
roles = new ArrayList<>();
}

/**
* 為用戶定義角色


*
* @param roles 角色集合
*/
public void add(String... roles) {
for (String role : roles) {
this.roles.add(role);
}
}

//getter setter 忽略。。。
}/<string>/<code>

<code>/**
* 買家
* Created by kingapex on 2018/3/11.
*
* @author kingapex
* @version 1.0
* @since 7.0.0
* 2018/3/11
*/
public class Buyer extends User {

/**
* 定義買家的角色
*/
public Buyer() {
this.add(Role.BUYER.name());
}


}

public class Seller extends Buyer {

/**
* 賣家id
*/
private Integer sellerId;

/**
* 賣家店鋪名稱
*/
private String sellerName;

/**
* 是否是自營 0 不是 1是
*/


private Integer selfOperated;


public Seller() {
//seller有 買家的角色和賣賓角色
add( Role.SELLER.name());
}
}

/**
* 管理員角色
*
* @author zh
* @version v7.0
* @date 18/6/27 上午10:09
* @since v7.0
*/

public class Admin extends User {

/**
* 是否是超級管理員
*/
private Integer founder;


/**
* 角色
*/
private List<string> roles;

//getter setter 忽略。。。

}/<string>/<code>

以上是javashop中權限體系中基礎的架構和思路以及相關源碼,因為篇幅關係,具體的權限校驗流程及代碼將在下一篇文章中分享。

易族智匯(javashop)原創文章