@@ -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); | |||||
} | |||||
} | |||||
} |