@@ -0,0 +1,131 @@ | |||
package com.xkcoding.rbac.security.common; | |||
import lombok.Data; | |||
import java.io.Serializable; | |||
/** | |||
* <p> | |||
* 通用的 API 接口封装 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.common | |||
* @description: 通用的 API 接口封装 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 14:55 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Data | |||
public class ApiResponse implements Serializable { | |||
private static final long serialVersionUID = 8993485788201922830L; | |||
/** | |||
* 状态码 | |||
*/ | |||
private Integer code; | |||
/** | |||
* 返回内容 | |||
*/ | |||
private String message; | |||
/** | |||
* 返回数据 | |||
*/ | |||
private Object data; | |||
/** | |||
* 无参构造函数 | |||
*/ | |||
private ApiResponse() { | |||
} | |||
/** | |||
* 全参构造函数 | |||
* | |||
* @param code 状态码 | |||
* @param message 返回内容 | |||
* @param data 返回数据 | |||
*/ | |||
private ApiResponse(Integer code, String message, Object data) { | |||
this.code = code; | |||
this.message = message; | |||
this.data = data; | |||
} | |||
/** | |||
* 构造一个自定义的API返回 | |||
* | |||
* @param code 状态码 | |||
* @param message 返回内容 | |||
* @param data 返回数据 | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse of(Integer code, String message, Object data) { | |||
return new ApiResponse(code, message, data); | |||
} | |||
/** | |||
* 构造一个成功且不带数据的API返回 | |||
* | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse ofSuccess() { | |||
return ofSuccess(null); | |||
} | |||
/** | |||
* 构造一个成功且带数据的API返回 | |||
* | |||
* @param data 返回数据 | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse ofSuccess(Object data) { | |||
return ofStatus(Status.SUCCESS, data); | |||
} | |||
/** | |||
* 构造一个成功且自定义消息的API返回 | |||
* | |||
* @param message 返回内容 | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse ofMessage(String message) { | |||
return of(Status.SUCCESS.getCode(), message, null); | |||
} | |||
/** | |||
* 构造一个有状态的API返回 | |||
* | |||
* @param status 状态 {@link Status} | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse ofStatus(Status status) { | |||
return ofStatus(status, null); | |||
} | |||
/** | |||
* 构造一个有状态且带数据的API返回 | |||
* | |||
* @param status 状态 {@link IStatus} | |||
* @param data 返回数据 | |||
* @return ApiResponse | |||
*/ | |||
public static ApiResponse ofStatus(IStatus status, Object data) { | |||
return of(status.getCode(), status.getMessage(), data); | |||
} | |||
/** | |||
* 构造一个异常的API返回 | |||
* | |||
* @param t 异常 | |||
* @param <T> {@link BaseException} 的子类 | |||
* @return ApiResponse | |||
*/ | |||
public static <T extends BaseException> ApiResponse ofException(T t) { | |||
return of(t.getCode(), t.getMessage(), t.getData()); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.xkcoding.rbac.security.common; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
/** | |||
* <p> | |||
* 异常基类 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.common | |||
* @description: 异常基类 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 14:57 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = true) | |||
public class BaseException extends RuntimeException { | |||
private Integer code; | |||
private String message; | |||
private Object data; | |||
public BaseException(Status status) { | |||
super(status.getMessage()); | |||
this.code = status.getCode(); | |||
this.message = status.getMessage(); | |||
} | |||
public BaseException(Status status, Object data) { | |||
this(status); | |||
this.data = data; | |||
} | |||
public BaseException(Integer code, String message) { | |||
super(message); | |||
this.code = code; | |||
this.message = message; | |||
} | |||
public BaseException(Integer code, String message, Object data) { | |||
this(code, message); | |||
this.data = data; | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.xkcoding.rbac.security.common; | |||
/** | |||
* <p> | |||
* REST API 错误码接口 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.common | |||
* @description: REST API 错误码接口 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 14:35 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public interface IStatus { | |||
/** | |||
* 状态码 | |||
* | |||
* @return 状态码 | |||
*/ | |||
Integer getCode(); | |||
/** | |||
* 返回信息 | |||
* | |||
* @return 返回信息 | |||
*/ | |||
String getMessage(); | |||
} |
@@ -0,0 +1,65 @@ | |||
package com.xkcoding.rbac.security.common; | |||
import lombok.Getter; | |||
/** | |||
* <p> | |||
* 通用状态码 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.common | |||
* @description: 通用状态码 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 14:31 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Getter | |||
public enum Status implements IStatus { | |||
/** | |||
* 操作成功 | |||
*/ | |||
SUCCESS(200, "操作成功"), | |||
/** | |||
* 操作异常 | |||
*/ | |||
ERROR(500, "操作异常"), | |||
/** | |||
* 退出成功 | |||
*/ | |||
LOGOUT(200, "退出成功"); | |||
/** | |||
* 状态码 | |||
*/ | |||
private Integer code; | |||
/** | |||
* 返回信息 | |||
*/ | |||
private String message; | |||
Status(Integer code, String message) { | |||
this.code = code; | |||
this.message = message; | |||
} | |||
public static Status fromCode(Integer code) { | |||
Status[] statuses = Status.values(); | |||
for (Status status : statuses) { | |||
if (status.getCode() | |||
.equals(code)) { | |||
return status; | |||
} | |||
} | |||
return SUCCESS; | |||
} | |||
@Override | |||
public String toString() { | |||
return String.format(" Status:{code=%s, message=%s} ", getCode(), getMessage()); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
package com.xkcoding.rbac.security.config; | |||
import lombok.Data; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
/** | |||
* <p> | |||
* JWT 配置 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.config | |||
* @description: JWT 配置 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 13:42 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@ConfigurationProperties(prefix = "jwt.config") | |||
@Data | |||
public class JwtConfig { | |||
/** | |||
* jwt 加密 key, 默认值:xkcoding. | |||
*/ | |||
private String key = "xkcoding"; | |||
/** | |||
* jwt 过期时间, 默认值:600000 {@code 10 分钟}. | |||
*/ | |||
private Long ttl = 600000L; | |||
} |
@@ -0,0 +1,87 @@ | |||
package com.xkcoding.rbac.security.util; | |||
import cn.hutool.core.date.DateUtil; | |||
import com.xkcoding.rbac.security.config.JwtConfig; | |||
import io.jsonwebtoken.*; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||
import org.springframework.context.annotation.Configuration; | |||
import java.util.Date; | |||
/** | |||
* <p> | |||
* JWT 工具类 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.util | |||
* @description: JWT 工具类 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 13:42 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@EnableConfigurationProperties(JwtConfig.class) | |||
@Configuration | |||
@Slf4j | |||
public class JwtUtil { | |||
private final JwtConfig jwtConfig; | |||
@Autowired | |||
public JwtUtil(JwtConfig jwtConfig) { | |||
this.jwtConfig = jwtConfig; | |||
} | |||
/** | |||
* 创建JWT | |||
* | |||
* @param id 用户id | |||
* @param subject 用户名 | |||
* @param roles 用户角色 | |||
* @return JWT | |||
*/ | |||
public String createJWT(String id, String subject, String roles) { | |||
Date now = new Date(); | |||
JwtBuilder builder = Jwts.builder() | |||
.setId(id) | |||
.setSubject(subject) | |||
.setIssuedAt(now) | |||
.signWith(SignatureAlgorithm.HS256, jwtConfig.getKey()) | |||
.claim("roles", roles); | |||
if (jwtConfig.getTtl() > 0) { | |||
builder.setExpiration(DateUtil.offsetMillisecond(now, jwtConfig.getTtl() | |||
.intValue())); | |||
} | |||
return builder.compact(); | |||
} | |||
/** | |||
* 解析JWT | |||
* | |||
* @param jwt JWT | |||
* @return {@link Claims} | |||
*/ | |||
public Claims parseJWT(String jwt) { | |||
Claims claims = null; | |||
try { | |||
claims = Jwts.parser() | |||
.setSigningKey(jwtConfig.getKey()) | |||
.parseClaimsJws(jwt) | |||
.getBody(); | |||
} catch (ExpiredJwtException e) { | |||
log.error("Token 已过期"); | |||
} catch (UnsupportedJwtException e) { | |||
log.error("不支持的 Token"); | |||
} catch (MalformedJwtException e) { | |||
log.error("Token 无效"); | |||
} catch (SignatureException e) { | |||
log.error("无效的 Token 签名"); | |||
} catch (IllegalArgumentException e) { | |||
log.error("Token 参数不存在"); | |||
} | |||
return claims; | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.xkcoding.rbac.security.util; | |||
import cn.hutool.json.JSONUtil; | |||
import com.xkcoding.rbac.security.common.ApiResponse; | |||
import com.xkcoding.rbac.security.common.IStatus; | |||
import lombok.extern.slf4j.Slf4j; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
/** | |||
* <p> | |||
* Response 通用工具类 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.rbac.security.util | |||
* @description: Response 通用工具类 | |||
* @author: yangkai.shen | |||
* @date: Created in 2018-12-07 17:37 | |||
* @copyright: Copyright (c) 2018 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Slf4j | |||
public class ResponseUtil { | |||
/** | |||
* 往 response 写出 json | |||
* | |||
* @param response 响应 | |||
* @param status 状态 | |||
* @param data 返回数据 | |||
*/ | |||
public static void renderJson(HttpServletResponse response, IStatus status, Object data) { | |||
try { | |||
response.setHeader("Access-Control-Allow-Origin", "*"); | |||
response.setHeader("Access-Control-Allow-Methods", "*"); | |||
response.setContentType("application/json;charset=UTF-8"); | |||
response.setStatus(200); | |||
response.getWriter() | |||
.write(JSONUtil.toJsonStr(ApiResponse.ofStatus(status, data))); | |||
} catch (IOException e) { | |||
log.error("Response写出JSON异常,", e); | |||
} | |||
} | |||
} |