• (十一)jwt详解


    由于本人对于安全这块一知半解,所以简单的学习一下。

    JWT的简介:

    Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

    JWT(全称:JSON Web Token),在基于HTTP通信过程中,进行身份认证。

    JWT的构成:

    JWT是由三段信息构成的,将这三段信息文本用.连接一起就构成了JWT字符串。
    就像这样:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

    第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

    JWT的优点:

    1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快

    2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

    3.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。

    4.不需要在服务端保存会话信息,特别适用于分布式微服务。

    基于Token的身份认证 与 基于服务器的身份认证

    基于服务器的身份认证方式存在一些问题:

    • Sessions : 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。
    • Scalability : 由于Session是在内存中的,这就带来一些扩展性的问题。
    • CORS : 当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用AJAX调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。
    • CSRF : 用户很容易受到CSRF攻击。

    CSRF :一般指跨站请求伪造。

    JWT与Session的差异

    相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。

    Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。

    而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。

    Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

    主要流程如下:

    • 用户携带用户名和密码请求访问
    • 服务器校验用户凭据
    • 应用提供一个token给客户端
    • 客户端存储token,并且在随后的每一次请求中都带着它
    • 服务器校验token并返回数据

    注意:

    • 每一次请求都需要token
    • Token应该放在请求header中
    • 我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *

    用Token的好处

    无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。

    安全:Token不是Cookie。(The token, not a cookie.)每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话!

    还有一点,token在一段时间以后会过期,这个时候用户需要重新登录。这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权授予使特定的token甚至一组token无效。

    JWT与OAuth的区别

    OAuth2是一种授权框架 ,JWT是一种认证协议。

    无论使用哪种方式切记用HTTPS来保证数据的安全性。

    OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app)

    JWT是用在前后端分离, 需要简单的对后台API进行保护时使用.(前后端分离无session, 频繁传用户密码不安全)

    OAuth2是一个相对复杂的协议, 有4种授权模式, 其中的access code模式在实现时可以使用jwt才生成code, 也可以不用。

    获取token

    @Component
    public class TokenUtils {
        /**
         * 过期时间为一天
         * TODO 正式上线更换为15分钟
         */
        private static final long EXPIRE_TIME = 24*60*60*1000;
    
        /**
         * token私钥
         */
        private static final String TOKEN_SECRET = "joijsdfjlsjfljfljl5135313135";
    
        /**
         * 生成签名,15分钟后过期
         * @param username
         * @param username
         * @return
         */
        public static String createToken(String username) {
            try {
                Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
                Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
                // 附带username信息
                return JWT.create()
                        .withClaim("username", username)
                        //到期时间
                        .withExpiresAt(date)
                        //创建一个新的JWT,并使用给定的算法进行标记
                        .sign(algorithm);
            } catch (Exception e) {
                return null;
            }
        }
    
        public static boolean verity(String token){
            try {
                Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
                JWTVerifier verifier = JWT.require(algorithm).build();
                DecodedJWT jwt = verifier.verify(token);
                return true;
            } catch (IllegalArgumentException e) {
                return false;
            } catch (JWTVerificationException e) {
                return false;
            }
        }
        public static String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                return null;
            }
        }
        public static String sign(String username, String secret) {
            try {
                Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
                Algorithm algorithm = Algorithm.HMAC256(secret);
                // 附带username信息
                return JWT.create()
                        .withClaim("username", username)
                        .withExpiresAt(date)
                        .sign(algorithm);
            } catch (Exception e) {
                return null;
            }
        }
    }

    登录返回token

     @ApiOperation(value = "登录",notes="登录验证")
        @PostMapping(value = "/api/login")
        public Map<String, Object> login(@RequestBody User user) {
            Map<String, Object> map = new HashMap<>();
            // 对 html 标签进行转义,防止 XSS 攻击
            String username = user.getUsername();
            username = HtmlUtils.htmlEscape(username);
            ResponseDTO<Boolean> users = userService.login(username, user.getPassword());
            //返回token
            String token = TokenUtils.sign(username, user.getPassword());
            System.out.println(token);
            if (null == user) {
                map.put("code", "403");
                map.put("message","认证失败");
                return map;
            } else {
                //session.setAttribute("user", users);
                if (!StringUtils.isEmpty(token)) {
                    if (token != null){
                        map.put("code", "200");
                        map.put("message","认证成功");
                        map.put("token", token);
                        return map;
                    }
                    map.put("code", "403");
                    map.put("message","认证失败");
                    return map;
                }
            }
            map.put("code", "403");
            map.put("message","认证失败");
            return map;
        }
  • 相关阅读:
    JS对象、包装类
    js刷题网站
    typeof 返回的数据类型
    一文讲懂什么是函数柯里化,柯里化的目的及其代码实现
    JS 中深拷贝的几种实现方法
    JavaScript 开发的45个经典技巧
    JavaScript中的delete操作符
    IE下iframe不能正常加载,显示空白
    使用Costura.Fody插件将自己写的程序打包成一个可以独立运行的EXE文件
    list获取所有上级
  • 原文地址:https://www.cnblogs.com/changyuyao/p/14149589.html
Copyright © 2020-2023  润新知