b2b2c系統jwt權限源碼分享part1

需求分析

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

業務需求

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

技術需求

  • 去中心化

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

  • 公用能力抽象

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

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

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

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

架構思路

Token解析架構思路

b2b2c系統jwt權限源碼分享part1

  • 兩個接口分別對應token的解析和token的生成
  • 默認實現了一個jwt的實現類
  • 安全認證領域模型架構

    b2b2c系統jwt權限源碼分享part1

  • 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)原創文章


    b2b2c系統jwt權限源碼分享part1


    分享到:


    相關文章: