nimbus-jose-jwt 使用他可以生成或者解析对称加密或者非对称加密的的JWT. JWS是JWT规范的落地实现。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wnx.mall.tiny</groupId>
<artifactId>mall-tiny-jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mall-tiny-jwt</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--MyBatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
<!--集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.3</version>
</dependency>
<!--Mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--Swagger-UI API文档生产工具-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!--Hutool Java工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.7</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--JWT解析库-->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>8.16</version>
</dependency>
<!--Spring Security RSA工具类-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
<version>1.0.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>MyBatisConfig
Swagger2Config
</project>
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
mapper-locations:
- classpath:mapper/*.xml
- classpath*:com/**/mapper/*.xml
package com.wnx.mall.tiny.service;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.KeyLengthException;
import com.nimbusds.jose.jwk.RSAKey;
import com.wnx.mall.tiny.domain.PayloadDto;
import java.text.ParseException;
public interface JwtTokenService {
/**
* 使用HMAC算法生成TOken
* @param payloadStr
* @param secret
* @return
*/
String generateTokenByHMAC(String payloadStr,String secret) throws JOSEException;
/**
* 使用HMAC验证Token
* @param token
* @param secret
* @return
*/
PayloadDto verifyTokenByHMAC(String token,String secret) throws ParseException, JOSEException;
/**
* 使用RSA算法生成Token
* @param payloadStr
* @param rsaKey
* @return
*/
String generateTokenByRSA(String payloadStr,RSAKey rsaKey) throws JOSEException;
/**
* 使用RSA算法验证token
* @param token
* @param rsaKey
* @return
*/
PayloadDto verifyTokenByRSA(String token,RSAKey rsaKey) throws ParseException, JOSEException;
/**
* 获取默认的payload
* @return
*/
PayloadDto getDefaultPayloadDto();
/**
* 获取默认的RSAKey
*/
RSAKey getDefaultRSAKey();
}
package com.wnx.mall.tiny.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.wnx.mall.tiny.domain.PayloadDto;
import com.wnx.mall.tiny.exception.JwtExpiredException;
import com.wnx.mall.tiny.exception.JwtInvalidException;
import com.wnx.mall.tiny.service.JwtTokenService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.stereotype.Service;
import com.nimbusds.jose.jwk.RSAKey;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Date;
import java.util.UUID;
@Service
public class JwtTokenServiceImpl implements JwtTokenService {
@Override
public String generateTokenByHMAC(String payloadStr, String secret) throws JOSEException {
//创建JWS头,设置签名算法和类型
JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256)
.type(JOSEObjectType.JWT).build();
//将负载信息封装到Payload中
Payload payload = new Payload(payloadStr);
//创建JWS对象
JWSObject jwsObject = new JWSObject(jwsHeader,payload);
//创建HMAC签名器
JWSSigner jwsSigner = new MACSigner(secret);
//签名
jwsObject.sign(jwsSigner);
return jwsObject.serialize();
}
@Override
public PayloadDto verifyTokenByHMAC(String token, String secret) throws ParseException, JOSEException {
//从token中解析JWS对象
JWSObject jwsObject = JWSObject.parse(token);
//创建HMAC验证器
JWSVerifier jwsVerifier = new MACVerifier(secret);
if (!jwsObject.verify(jwsVerifier)){
throw new JwtInvalidException("token签名不合法");
}
String payload = jwsObject.getPayload().toString();
System.out.println(payload);
PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class);
if (payloadDto.getExp() < new Date().getTime()){
throw new JwtExpiredException("token已过期!");
}
return payloadDto;
}
@Override
public String generateTokenByRSA(String payloadStr, RSAKey rsaKey) throws JOSEException {
//创建JWS头,设置签名算法和类型
JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
.type(JOSEObjectType.JWT)
.build();
//将负载信息封装到payload中
Payload payload = new Payload(payloadStr);
//创建JWS对象
JWSObject jwsObject = new JWSObject(jwsHeader,payload);
//创建RSA签名器
JWSSigner jwsSigner = new RSASSASigner(rsaKey,true);
//签名
jwsObject.sign(jwsSigner);
return jwsObject.serialize();
}
@Override
public PayloadDto verifyTokenByRSA(String token, RSAKey rsaKey) throws ParseException, JOSEException {
//从token中解析JWS对象
JWSObject jwsObject = JWSObject.parse(token);
RSAKey publicRsaKey = rsaKey.toPublicJWK();
//使用RSA公钥创建RSA验证器
JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
if (!jwsObject.verify(jwsVerifier)){
throw new JwtInvalidException("token签名不合法!");
}
String payload = jwsObject.getPayload().toString();
PayloadDto payloadDto = JSONUtil.toBean(payload, PayloadDto.class);
if (payloadDto.getExp() < new Date().getTime()){
throw new JwtExpiredException("token已过期!");
}
return payloadDto;
}
@Override
public PayloadDto getDefaultPayloadDto() {
Date now = new Date();
Date exp = DateUtil.offsetSecond(now,60*60);
return PayloadDto.builder()
.sub("macro")
.iat(now.getTime())
.exp(exp.getTime())
.jti(UUID.randomUUID().toString())
.username("macro")
.authorities(CollUtil.toList("ADMIN"))
.build();
}
@Override
public RSAKey getDefaultRSAKey() {
//从classpath下获取RSA密钥对
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
//获取RSA公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//获取RSA私钥
PrivateKey privateKey = keyPair.getPrivate();
return new RSAKey.Builder(publicKey).privateKey(privateKey).build();
}
}
package com.wnx.mall.tiny.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class PayloadDto {
@ApiModelProperty("主题")
private String sub;
@ApiModelProperty("签发时间")
private Long iat;
@ApiModelProperty("过期时间")
private Long exp;
@ApiModelProperty("JWT的ID")
private String jti;
@ApiModelProperty("用户名称")
private String username;
@ApiModelProperty("用户拥有的权限")
private List<String> authorities;
}
package com.wnx.mall.tiny.exception;
public class JwtExpiredException extends RuntimeException{
public JwtExpiredException(String message) {
super(message);
}
}
package com.wnx.mall.tiny.exception;
public class JwtInvalidException extends RuntimeException{
public JwtInvalidException(String message) {
super(message);
}
}
package com.wnx.mall.tiny.controller;
import com.wnx.mall.tiny.common.api.CommonPage;
import com.wnx.mall.tiny.common.api.CommonResult;
import com.wnx.mall.tiny.mbg.model.PmsBrand;
import com.wnx.mall.tiny.service.PmsBrandService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 品牌管理Controller
* Created by macro on 2019/4/19.
*/
@Api(tags = "PmsBrandController", description = "商品品牌管理")
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService brandService;
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@ApiOperation("获取所有品牌列表")
@RequestMapping(value = "listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult<List<PmsBrand>> getBrandList() {
return CommonResult.success(brandService.listAllBrand());
}
@ApiOperation("添加品牌")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
CommonResult commonResult;
int count = brandService.createBrand(pmsBrand);
if (count == 1) {
commonResult = CommonResult.success(pmsBrand);
LOGGER.debug("createBrand success:{}", pmsBrand);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("createBrand failed:{}", pmsBrand);
}
return commonResult;
}
@ApiOperation("更新指定id品牌信息")
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
CommonResult commonResult;
int count = brandService.updateBrand(id, pmsBrandDto);
if (count == 1) {
commonResult = CommonResult.success(pmsBrandDto);
LOGGER.debug("updateBrand success:{}", pmsBrandDto);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
}
return commonResult;
}
@ApiOperation("删除指定id的品牌")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult deleteBrand(@PathVariable("id") Long id) {
int count = brandService.deleteBrand(id);
if (count == 1) {
LOGGER.debug("deleteBrand success :id={}", id);
return CommonResult.success(null);
} else {
LOGGER.debug("deleteBrand failed :id={}", id);
return CommonResult.failed("操作失败");
}
}
@ApiOperation("分页查询品牌列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("页码") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每页数量") Integer pageSize) {
List<PmsBrand> brandList = brandService.listBrand(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
@ApiOperation("获取指定id的品牌详情")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
return CommonResult.success(brandService.getBrand(id));
}
}
3.2、Swagger-UI
http://localhost:8080/swagger-ui.html/