JWT入门教程

概述

安全的重要性。

简介

JWT,JSON Web Token,开放的、行业标准(RFC 7519),用于网络应用环境间安全传递声明。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的业务逻辑所须的声明信息。
特点:

  • 跨语言:支持主流语言
  • 自包含:包含必要的所有信息,如用户信息和签名等
  • 易传递:很方便通过HTTP头部传递

组成

JWT的token是三段由小数点分隔组成的字符串:header.payload.signature

  1. header
    头部包含两部分:声明类型和使用的哈希算法(通常直接使用HMAC SHA256,就是HS256)
{
    
    
"typ": "JWT",
"alg": "HS256"
}

将头部进行base64编码构成第一部分。Base64是一种用64个字符来表示任意二进制数据的方法,Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据。

  1. payload
    也称为JWT claims,放置需要传输的信息,有三类:
  • 保留claims,主要包括iss发行者、exp过期时间、sub主题、aud用户等
  • 公共claims,定义新创的信息,比如用户信息和其他重要信息
  • 私有claims,用于发布者和消费者都同意以私有的方式使用的信息
{
    
    
"iss": "jwt.io",
"exp": 1496199995458,
"name": "sinwaj",
"role": "admin"," 
}
  1. signature
    需要采用编码的header、编码的payload、secret,使用header中指定的算法进行签名。

实战

package com.aaa.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

public class JwtUtil {
    
    

    private final static String base64Security = "";

    private final static String clientId = "";

    private final static String jwtName = "restapiuser";

    /**
     * 过期时间,2天
     */
    private final static long TTLMillis = 172800 * 1000;

    /**
     * 解析jwt
     */
    public static Claims parseJWT(String jsonWebToken) {
    
    
        try {
    
    
            return Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
                    .parseClaimsJws(jsonWebToken).getBody();
        } catch (Exception e) {
    
    
            return null;
        }
    }

    public static String createJWT(String name, Integer userId) {
    
    
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //生成签名密钥
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        // 添加构成JWT的参数
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                .claim("unique_name", name)
                .claim("userid", userId)
                .setIssuer(jwtName)
                .setAudience(clientId)
                .signWith(signatureAlgorithm, signingKey);
        // 添加Token过期时间
        if (TTLMillis >= 0) {
    
    
            long expMillis = nowMillis + TTLMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }
        // 生成JWT
        return builder.compact();
    }

    public static Claims getUserInfo() {
    
    
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        final String authHeader = request.getHeader("authorization");
        final String token = authHeader.substring(7);
        try {
    
    
            return JwtUtil.parseJWT(token);
        } catch (Exception e) {
    
    
            return null;
        }
    }
}
package com.aaa.filter;

import com.aaa.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JWTInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        final String authHeader = request.getHeader("authorization");
        String url = request.getRequestURL().toString();
        String[] urls = url.split("/");
        String root = urls[0] + "/" + urls[1] + "/" + urls[2];
        // OPTIONS方法放行
        if ("OPTIONS".equals(request.getMethod())) {
    
    
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        } else {
    
    
            boolean checkAuth = null == authHeader || !authHeader.startsWith("Bearer") || authHeader.length() < 7;
            if (checkAuth) {
    
    
                response.sendRedirect(root);
                return false;
            }
        }
        final String token = authHeader.substring(7);
        try {
    
    
            final Claims claims = JwtUtil.parseJWT(token);
            if (claims == null) {
    
    
                response.sendRedirect(root);
                return false;
            }
            request.setAttribute("CLAIMS", claims);
            return true;
        } catch (final Exception e) {
    
    
            response.sendRedirect(root);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
    
    
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    
    
    }

}

package com.aaa.service.user.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.aaa.dao.UserMapper;
import com.aaa.model.User;
import com.aaa.service.user.UserService;
import com.aaa.utils.DomainUtil;
import com.aaa.utils.JwtUtil;
import com.aaa.utils.ServiceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    
    

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    private String domainIp;

    private String domianPort;

    @Autowired
    UserMapper userMapper;


    @Override
    public String login(JSONObject jsonObject) {
    
    
        String name = (String) jsonObject.get("userName");
        String pass = (String) jsonObject.get("password");
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(pas)) {
    
    
            return JSONObject.toJSONString(ServiceUtil.returnError("用户名或密码不能为空!"));
        }
        try {
    
    
            User user = new User(name, "1", true);
            // 手动在db里面配置新增用户
            User userInfo = userMapper.selectBySelectiveFields(user);
            if (userInfo == null) {
    
    
                return JSONObject.toJSONString(ServiceUtil.returnError("用户名不存在!"));
            }
            Boolean status = DomainUtil.checkDomain("CORP\\" + name, pass, domainIp, domainPort);
            if (status) {
    
    
                String jwtToken = JwtUtil.createJWT(userInfo.getUserName(), userInfo.getId());
                JSONObject data = new JSONObject();
                data.put("jwtToken", jwtToken);
                data.put("roleId", userMapper.getUserRole(userInfo.getId()));
                return JSONObject.toJSONString(ServiceUtil.returnSuccessData(data));
            } else {
    
    
                return JSONObject.toJSONString(ServiceUtil.returnError("用户名或者密码错误!"));
            }
        } catch (Exception e) {
    
    
            return JSONObject.toJSONString(ServiceUtil.returnError("系统异常,请稍后再试!"));
        }
    }

}
/**
 * 内网ldap账户认证
 */
public static Boolean checkDomain(String userName, String password, String domainIp, String domainPort) {
    
    
    String url = "ldap://" + domainIp + ":" + domainPort;
    Hashtable<String, String> env = new Hashtable<>();
    javax.naming.directory.DirContext ctx;
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    env.put(Context.PROVIDER_URL, url);
    env.put(Context.SECURITY_PRINCIPAL, userName);
    env.put(Context.SECURITY_CREDENTIALS, password);

    try {
    
    
    	// 初始化上下文
        ctx = new javax.naming.directory.InitialDirContext(env);
        ctx.close();
        // 验证成功返回name
        return true;
    } catch (javax.naming.AuthenticationException e) {
    
    
        logger.error("认证失败:" + e.getMessage());
        return false;
    } catch (Exception e) {
    
    
        logger.error("认证出错:" + e.getMessage());
        return false;
    }
}

对比

token的认证和session认证

猜你喜欢

转载自blog.csdn.net/lonelymanontheway/article/details/107373397