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


    分享到:


    相關文章: