zoukankan      html  css  js  c++  java
  • 57 Spring Cloud基于JWT创建统一的认证服务

    认证服务肯定要有用户信息,不然怎么认证是否为合法用户?因为是内部的调用认证,可以简单一点,用数据库管理就是一种方式。或者可以配置用户信息,然后集成分布式配置管理就完美了。

    表结构

    本教程中的案例把查数据库这一步骤省略了,大家可以自行补充,但是表的设计还是要跟大家讲解的。用户表的形式如图 1 所示。

     相关的代码如下所示。

    create table auth_user(
      id int(4) not null,
      accessKey varchar(100) not null,
      secretKey varchar(100) not null,
      Primary key (id)
    );
    Alter table auth_user comment '认证用户信息表';
    

    这里只有简单的几个字段,若大家有别的需求可以自行去扩展。代码中的 accessKey 和 secretKey 是用户身份的标识。  

    JWT 工具类封装

    JWT 的 GitHub 地址是:https://github.com/jwtk/jjwt,依赖配置代码如下所示。

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
    </dependency>
    

    生成 Token 是在进行用户身份认证之后,通过用户的 ID 来生成一个 Token,这个 Token 采用 RSA 加密的方式进行加密,Token 的内容包括用户的 ID 和过期时间。

    检查 Token 则是根据调用方带来的 Token 检查是否为合法用户,就是对 Token 进行解密操作,能解密并且在有效期内表示合法,合法则返回用户 ID。

    刷新 RSA 公钥及私钥的作用是防止公钥、私钥泄露,公钥、私钥一般是写死的,不过我们可以做成配置的。集成配置管理中心后,可以对公钥、私钥进行动态修改,修改之后需要重新初始化公钥、私钥的对象信息。  

    获取 Token 代码如下所示。

    /**
    * 获取 Token
    *
    * @param uid 用户 ID
    * @param exp 失效时间, 单位分钟
    * @return
    */
    public static String getToken(String uid, int exp) {
        Long endTime = System.currentTimeMillis() + 1000 * 60 * exp;
        return Jwts.builder().setSubject(uid).setExpiration(new Date(endTime))
                .signWith(SignatureAlgorithm.RS512, priKey).compact();
    }
    

      

    检查 Token 是否合法代码如下所示。

    /**
    * 检查 Token 是否合法
    *
    * @param token
    * @return JWTResult
    */
    public JWTResult checkToken(String token) {
        try {
            Claims claims = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(token).getBody();
            String sub = claims.get("sub", String.class);
            return new JWTResult(true, sub, "合法请求", ResponseCode.SUCCESS_CODE.getCode());
        } catch (ExpiredJwtException e) {
            // 在解析 JWT 字符串时, 如果'过期时间字段'已经早于当前时间,
            // 将会抛出 ExpiredJwtException 异常, 说明本次请求已经失效
            return new JWTResult(false, null, "token已过期 ", ResponseCode.TOKEN_TIMEOUT_CODE.getCode());
        } catch (SignatureException e) {
            // 在解析 JWT 字符串时, 如果密钥不正确, 将会解析失败, 抛出
            // SignatureException 异常, 说明该 JWT 字符串是伪造的
            return new JWTResult(false, null, "非法请求", ResponseCode.NO_AUTH_CODE.getCode());
        } catch (Exception e) {
            return new JWTResult(false, null, "非法请求", ResponseCode.NO_AUTH_CODE.getCode());
        }
    }
    

      

    完整代码如下所示。

    /**
    * API调用认证工具类,采用RSA加密
    */
    public class JWTUtils {
        private static RSAPrivateKey priKey;
        private static RSAPublicKey pubKey;
        private static class SingletonHolder {
            private static final JWTUtils INSTANCE = new JWTUtils();
        }
        public synchronized static JWTUtils getInstance(String modulus, String privateExponent, String publicExponent) {
            if (priKey == null && pubKey == null) {
                priKey = RSAUtils.getPrivateKey(modulus, privateExponent);
                pubKey = RSAUtils.getPublicKey(modulus, publicExponent);
            }
            return SingletonHolder.INSTANCE;
        }
        public synchronized static void reload(String modulus, String privateExponent, String publicExponent) {
            priKey = RSAUtils.getPrivateKey(modulus, privateExponent);
            pubKey = RSAUtils.getPublicKey(modulus, publicExponent);
        }
        public synchronized static JWTUtils getInstance() {
            if (priKey == null && pubKey == null) {
                priKey = RSAUtils.getPrivateKey(RSAUtils.modulus, RSAUtils.private_exponent);
                pubKey = RSAUtils.getPublicKey(RSAUtils.modulus, RSAUtils.public_exponent);
            }
            return SingletonHolder.INSTANCE;
        }
        /**
         * 获取Token
         *
         * @param uid 用户ID
         * @param exp 失效时间,单位分钟
         * @return
         */
        public static String getToken(String uid, int exp) {
            long endTime = System.currentTimeMillis() + 1000 * 60 * exp;
            return Jwts.builder().setSubject(uid).setExpiration(new Date(endTime))
                    .signWith(SignatureAlgorithm.RS512, priKey).compact();
        }
        /**
         * 获取Token
         *
         * @param uid 用户ID
         * @return
         */
        public String getToken(String uid) {
            long endTime = System.currentTimeMillis() + 1000 * 60 * 1440;
            return Jwts.builder().setSubject(uid).setExpiration(new Date(endTime))
                    .signWith(SignatureAlgorithm.RS512, priKey).compact();
        }
        /**
         * 检查Token是否合法
         *
         * @param token
         * @return JWTResult
         */
        public JWTResult checkToken(String token) {
            try {
                Claims claims = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(token).getBody();
                String sub = claims.get("sub", String.class);
                return new JWTResult(true, sub, "合法请求", ResponseCode.SUCCESS_CODE.getCode());
            } catch (ExpiredJwtException e) {
                // 在解析JWT字符串时,如果‘过期时间字段’已经早于当前时间,将会抛出ExpiredJwtException异常,说明本次请求已经失效
                return new JWTResult(false, null, "token已过期", ResponseCode.TOKEN_TIMEOUT_CODE.getCode());
            } catch (SignatureException e) {
                // 在解析JWT字符串时,如果密钥不正确,将会解析失败,抛出SignatureException异常,说明该JWT字符串是伪造的
                return new JWTResult(false, null, "非法请求", ResponseCode.NO_AUTH_CODE.getCode());
            } catch (Exception e) {
                return new JWTResult(false, null, "非法请求", ResponseCode.NO_AUTH_CODE.getCode());
            }
        }
        public static class JWTResult {
            private boolean status;
            private String uid;
            private String msg;
            private int code;
            public JWTResult() {
                super();
            }
            public JWTResult(boolean status, String uid, String msg, int code) {
                super();
                this.status = status;
                this.uid = uid;
                this.msg = msg;
                this.code = code;
            }
            public int getCode() {
                return code;
            }
            public void setCode(int code) {
                this.code = code;
            }
            public String getMsg() {
                return msg;
            }
            public void setMsg(String msg) {
                this.msg = msg;
            }
            public boolean isStatus() {
                return status;
            }
            public void setStatus(boolean status) {
                this.status = status;
            }
            public String getUid() {
                return uid;
            }
            public void setUid(String uid) {
                this.uid = uid;
            }
        }
    }
    

    认证接口

    认证接口用于调用方进行认证时,认证通过则返回一个加密的 Token 给对方,对方就可以用这个 Token 去请求别的服务了,认证获取 Token 代码如下所示。  

    @PostMapping("/token")
    public ResponseData auth(@RequestBody AuthQuery query) throws Exception {
        if (StringUtils.isBlank(query.getAccessKey()) || StringUtils.isBlank(query.getSecretKey())) {
            return ResponseData.failByParam("accessKey and secretKey not null");
        }
        User user = authService.auth(query);
        if (user == null) {
            return ResponseData.failByParam(" 认证失败 ");
        }
        JWTUtils jwt = JWTUtils.getInstance();
        return ResponseData.ok(jwt.getToken(user.getId().toString()));
    }
    

    认证参数代码如下所示。  

    /**
    * API 用户认证参数类
    */
    public class AuthQuery {
        private String accessKey;
        private String secretKey;
        // get set ...
    }
    

    authService 中的 auth 方法就是根据 accessKey 和 secretKey 判断是否有这个用户。  

  • 相关阅读:
    The Fifth Week Lucklyzpp
    The Fourth Week Lucklyzpp
    The Third Week Lucklyzpp
    The Second Week lucklyzpp
    快10年没怎么过来了,一切如常
    男女诗篇
    ubuntu安装mysql
    ubuntu配置tomcat和jdk
    ubuntu常用操作命令以及它的通道模式简解
    Ubuntu操作异常汇总
  • 原文地址:https://www.cnblogs.com/jrkl/p/14442446.html
Copyright © 2011-2022 走看看