springboot——基于JWT的token认证

一.介绍java

一、JSON Web Token,简称JWT,本质是一个token,是一种紧凑的URL安全方法,用于在网络通讯的双方之间传递。web

二、通常放在HTTP的headers 参数里面的authorization里面,值的前面加Bearer关键字和空格。spring

三、主要用于身份认证和信息交换apache

四、由三部分组成,用英文句点链接(.),例如:xxxxxx.yyyyyy.zzzzzzjson

——认证呢,有不少种,session 认证,basic auth 认证,OAuth认证,token认证等。这里只是介绍了其中基于JWT技术的token认证而已。用token认证,很好解决大型分布式横行扩展问题。api

2、JWT的结构语法:https://blog.csdn.net/buyaoshuohua1/article/details/73739419/安全

3、模仿登陆+过滤器(springboot demo)springboot

(1)流程:服务器

登陆接口未作拦截建立token网络

非登陆接口被过滤器拦截>>验证token是否存在,是否失效>>验证成功则返回,不然就返回

(2)实现:

一、添加依赖

<!--JJWT库-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>

二、TokenObject

package com.example.demo.common.bo;

import org.springframework.stereotype.Component;

@Component
public class TokenObject {
	/**客户端id*/
    private String clientId;
    /**base64加密*/
    private String base64Secret;
    /**用户名*/
    private String name;
    /**到期时间*/
    private long expiresSecond;
    /**管理员名称*/
    private String userName;
    /**管理员id*/
    private Integer aId;
    /**职能*/
    private String role;
    /**项目名称*/
    private String project;
	public String getClientId() {
		return clientId;
	}
	public void setClientId(String clientId) {
		this.clientId = clientId;
	}
	public String getBase64Secret() {
		return base64Secret;
	}
	public void setBase64Secret(String base64Secret) {
		this.base64Secret = base64Secret;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getExpiresSecond() {
		return expiresSecond;
	}
	public void setExpiresSecond(long expiresSecond) {
		this.expiresSecond = expiresSecond;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public Integer getaId() {
		return aId;
	}
	public void setaId(Integer aId) {
		this.aId = aId;
	}
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	public String getProject() {
		return project;
	}
	public void setProject(String project) {
		this.project = project;
	}
    

}

三、JwtHelper

package com.example.demo.common.utils;

import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.common.bo.TokenObject;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;

public class JwtHelper {
	private static Logger logger = LoggerFactory.getLogger(JwtHelper.class);

    /**
     * 校验Token
     * @param jwt
     * @param httpRequest
     * @return
     */
    public static int checkToken(String jwt, HttpServletRequest httpRequest){
        if (!StringUtils.isBlank(jwt)){
            if (jwt.split("\\.").length==3) {
                logger.info("jwt:" + jwt);
                String[] split = jwt.split("\\.");
                String content = split[1];//负荷
                String s = Base64Codec.BASE64URL.decodeToString(content);
                logger.info("s:" + s);
                String sign = split[2];//签名
                logger.info("sign:" + sign);
                JSONObject jsonObject1 = JSONObject.parseObject(s);

                long nowMillis = System.currentTimeMillis();
                Date now = new Date(nowMillis);
                long expiresSecond = (long) jsonObject1.get("expiresSecond");

                //判断是否过时
                if(now.getTime()>expiresSecond)
                     return 2;


                TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
//                 if (o!=null){
//                    String project = o.getProject();
//                   // if (!StaticInfo.PROJECT.equals(project))
//                        return 0;
//                }
                String jwtByStr = createJWTByObj(o);
                String s2 = jwtByStr.split("\\.")[2];
                logger.info("s2:" + s2);
                if (sign.equals(s2)) {
                	logger.info("验证成功");
                    return 1;
                } else
                    return 0;
            }
        }
        return 0;
    }

    /**
     * 获取用户id
     * @param jwt
     * @return
     */
    public static int  getIdByJWT(String jwt){
        if (!StringUtils.isBlank(jwt)) {
            if (jwt.split("\\.").length == 3) {
                logger.info("jwt:" + jwt);
                String[] split = jwt.split("\\.");
                String content = split[1];
                String s = Base64Codec.BASE64URL.decodeToString(content);
                JSONObject jsonObject1 = JSONObject.parseObject(s);
                TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
                return o.getaId();
            }
        }
        return 0;
    }
    
    /**
     * 获取用户clinetId
     * @param jwt
     * @return
     */
    public static String  getClinetIdByJWT(String jwt){
        if (!StringUtils.isBlank(jwt)) {
            if (jwt.split("\\.").length == 3) {
                logger.info("jwt:" + jwt);
                String[] split = jwt.split("\\.");
                String content = split[1];
                String s = Base64Codec.BASE64URL.decodeToString(content);
                JSONObject jsonObject1 = JSONObject.parseObject(s);
                TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
                return o.getClientId();
            }
        }
        return "";
    }

    /**
     * 获取客户信息
     * @param request
     * @return
     * @throws CustomException
     */
    public static int getIdByRequest(HttpServletRequest request) {
        int i = 0;
        String auth = request.getHeader("Authorization");
        if ((auth != null) && (auth.length() > 6)) {
            String HeadStr = auth.substring(0, 5).toLowerCase();
            if (HeadStr.compareTo("basic") == 0) {
                auth = auth.substring(6, auth.length());
                i = JwtHelper.getIdByJWT(auth);
            }
        }
        if (i==0)
           // throw new CustomException(ResultEnum.PERMISSION_DENIED);
		return i;
		return i;
    }

    public static String createJWTByObj(TokenObject tokenObject) {
        Object jsonObject = JSONObject.toJSON(tokenObject);
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

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

        //生成签名密钥
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(tokenObject.getBase64Secret());//服务器本身定义的秘钥
        SecretKeySpec signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        //添加构成JWT的参数
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setPayload(jsonObject.toString())
                .signWith(signatureAlgorithm, signingKey);

        //生成JWT
        return builder.compact();
    }

}

四、AuthUtil

public class AuthUtil {
	public static String getClinetIdByAuth(HttpServletRequest request) {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String auth = httpRequest.getHeader("Authorization");
		auth = auth.substring(7, auth.length());
		return JwtHelper.getClinetIdByJWT(auth);
	}

}

五、HTTPBasicAuthorizeAttribute

@Order(value = 1) 
@WebFilter(filterName = "colationFilter", urlPatterns = "/testtoken/*")
public class HTTPBasicAuthorizeAttribute implements Filter {

	private Logger logger = LoggerFactory.getLogger(HTTPBasicAuthorizeAttribute.class);

	@Override
	public void destroy() {
		logger.info("后台token过滤器,溜了溜了溜了溜了");
		// 能够日志管理添加
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		logger.info("后台token过滤器检测");
		// 1.检测当前是否须要从新登陆
		// if(audience!=null){
		// if (audience.getClientId().equals(StaticInfo.SIGNOUT)){
		// toResponse((HttpServletResponse) response,1,(HttpServletRequest)
		// request);
		// return;
		// }
		// }
		// 2.检测请求同token信息
		ResultEnum resultStatusCode = checkHTTPBasicAuthorize(request);
		if (resultStatusCode.equals(ResultEnum.INVALID_SINGTIMEOUT)) {// 超时
			toResponse((HttpServletResponse) response, 2, (HttpServletRequest) request);
			return;
		} else if (resultStatusCode.equals(ResultEnum.INVALID_PERMISSION_DENIED)) {// 权限不够
			toResponse((HttpServletResponse) response, 0, (HttpServletRequest) request);
			return;
		}
		logger.info("后台token过滤器检测经过");
		chain.doFilter(request, response);
	}

	/**
	 * 响应
	 * 
	 * @param response
	 * @param i
	 *            类型
	 * @throws IOException
	 */
	private void toResponse(HttpServletResponse response, int i, HttpServletRequest request) throws IOException {
		HttpServletResponse httpResponse = response;
		httpResponse.setCharacterEncoding("UTF-8");
		httpResponse.setContentType("application/json; charset=utf-8");
		httpResponse.setHeader("Access-Control-Allow-Origin", "*");
		httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
		httpResponse.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PATCH,PUT");
		httpResponse.setHeader("Access-Control-Max-Age", "3600");
		httpResponse.setHeader("Access-Control-Allow-Headers",
				"Origin,X-Requested-With,x-requested-with,X-Custom-Header," + "Content-Type,Accept,Authorization");
		String method = request.getMethod();
		if ("OPTIONS".equalsIgnoreCase(method)) {
			logger.info("OPTIONS请求");
			httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
		}

		ObjectMapper mapper = new ObjectMapper();
		PrintWriter writer = httpResponse.getWriter();
		// if (i==1)
		// writer.write(mapper.writeValueAsString(ResultEnum.RESTARTLOGIN));
		if (i == 2)
			writer.write(mapper.writeValueAsString(ResultEnum.INVALID_SINGTIMEOUT));
		else if (i == 0)
			writer.write(mapper.writeValueAsString(ResultEnum.INVALID_PERMISSION_DENIED));

		writer.close();

		if (writer != null)
			writer = null;
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		logger.info("后台token过滤器启动");
	}

	/**
	 * 检测请求同token信息
	 * 
	 * @param request
	 * @return
	 */
	private ResultEnum checkHTTPBasicAuthorize(ServletRequest request) {
		try {
			HttpServletRequest httpRequest = (HttpServletRequest) request;
			String auth = httpRequest.getHeader("Authorization");
			if ((auth != null) && (auth.length() > 7)) {
				String HeadStr = auth.substring(0, 6).toLowerCase();
				/*if (HeadStr.compareTo("basic") == 0) {
					auth = auth.substring(6, auth.length());
					int i = JwtHelper.checkToken(auth, httpRequest);
					if (i == 1) {
						return ResultEnum.OK;
					} else if (i == 2) {
						return ResultEnum.INVALID_SINGTIMEOUT;
					} else if (i == 0) {
						return ResultEnum.INVALID_PERMISSION_DENIED;
					}
				}*/
				if (HeadStr.compareTo("bearer") == 0) {

					auth = auth.substring(7, auth.length());
					int i = JwtHelper.checkToken(auth, httpRequest);
					if (i == 1) {
						return ResultEnum.OK;
					} else if (i == 2) {
						return ResultEnum.INVALID_SINGTIMEOUT;
					} else if (i == 0) {
						return ResultEnum.INVALID_PERMISSION_DENIED;
					}
				}
			}
			return ResultEnum.INVALID_PERMISSION_DENIED;
		} catch (Exception ex) {
			return ResultEnum.INVALID_PERMISSION_DENIED;
		}

	}

}

六、LoginController

@Api(value = "LoginController")
@RequestMapping("/api")
@Controller
public class LoginController {

	@ApiOperation(value = "登陆测试方法", notes = "登陆测试方法")
	@RequestMapping(value = "/login", method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResultModel<String> login(@RequestBody String userName) {
		System.out.println("用户名:"+userName);
		// 一、业务逻辑
		// 二、返回token
		TokenObject token = new TokenObject();
		token.setBase64Secret("test");
		token.setClientId(userName);
		token.setExpiresSecond(new Date().getTime() + 5*60 * 1000);
		return ResultModel.success(JwtHelper.createJWTByObj(token));
	}

}

七、TestTokenController

@Api(value = "TestTokenController")
@RequestMapping("/testtoken")
@RestController
public class TestTokenController {

	@ApiOperation(value = "token测试方法", notes = "token测试方法")
	@RequestMapping(value = "/test", method = RequestMethod.POST)
	public ResultModel<String> test(String p,HttpServletRequest request) {
		return ResultModel.success(AuthUtil.getClinetIdByAuth(request));
	}

}

(3)效果:

登陆:

用token访问业务接口: