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的依賴:

/**
 * 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>  T parse(Class<T> clz, String token) throws TokenParseException;
}

 

 

TokenManagerImpl

token業務類基於jwt的實現:

/**
 * 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> T parse(Class<T> clz, String token) throws TokenParseException {
        JwtTokenParser tokenParser = new JwtTokenParser(javashopConfig.getTokenSecret());
        return tokenParser.parse(clz, token);
    }
}

 

Token創建接口

/**
 * 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);

}

 

Token 解析器

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

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

}

JwtTokenCreater

基於jwt token的創建實現:

/**
 * 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;
}

JwtTokenParser

基於jwt的token解析器

 /**

 * 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> T parse(Class<T> 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);
        }

    }

 

AuthUser

認證用戶接口

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

    List<String> getRoles();

    void setRoles(List<String> roles);
}

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

User:

基類

/**
 * 用戶
 * 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 忽略。。。
}

 

/**
 * 買家
 * 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 忽略。。。
    
}

 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章