山海Power
# 1. 组件能力
支持用户进行互斥鉴权
基于注解模式的权限校验
基于路由模式的权限校验
支持前后端分离模式下独立用户鉴权和会话数据读写
支持自定义权限集合实现和自定义路由集合实现
支持自行扩展缓存协议实现数据持久化(组件默认提供基于原生Redis协议的持久化组件)
集成OTP生成与校验核心模块,可以自行实现OTP绑定与校验能力
# 1.1 引入组件
<dependency>
<groupId>com.wangshanhai.power</groupId>
<artifactId>shanhai-power-spring-boot-starter</artifactId>
<version>${last.version}</version>
</dependency>
# 1.2 启用组件
SpringBoot 2.x 启用方式
@Configuration
@EnableShanHaiPower
public class ShanhaiConfig implements WebMvcConfigurer {
}
SpringBoot 1.5.x 启用方式
1)添加如下配置:
shanhai:
power:
auto-regist: false #低版本需要进行手动注册
2)自行完成对应组件注册,示例如下:
@Configuration
@EnableShanHaiPower
@EnableConfigurationProperties(ShanhaiPowerConfig.class)
@AutoConfigureAfter(WebMvcConfigurationSupport.class)
public class ShanhaiConfig extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ShanhaiPowerInterceptor()).addPathPatterns("/**");
//按需启用(注解权限)
//registry.addInterceptor(new ShanhaiPowerAnnotationPermissionsInterceptor()).addPathPatterns("/**");
//按需启用(路由权限)
//registry.addInterceptor(new ShanhaiPowerRoutePermissionsInterceptor()).addPathPatterns("/**");
}
}
3)组件说明:
组件 | 组件名称 | 组件说明 |
---|---|---|
ShanhaiPowerInterceptor | 用户身份鉴权组件 | 对用户登录的有效性进行鉴权 |
ShanhaiPowerAnnotationPermissionsInterceptor | 用户注解权限鉴权组件 | 对用户进行单一资源有效性进行鉴权 |
ShanhaiPowerRoutePermissionsInterceptor | 用户路由权限鉴权组件 | 对用户进行路由资源有效性进行鉴权 |
# 1.3 配置说明
shanhai:
power:
tokenName: token #Token名称
tokenAlgorithm: uuid # Token生成算法
token-prefix: 'shanhai ' #Token前缀
exclusive-login: true #同端互斥登录 (默认为false)
auth-path-patterns: #鉴权组件拦截范围
- /**
auth-exclude-path-patterns: #鉴权组件拦截白名单范围
- /api/xxx
# 1.4 用户会话组件
API说明 | API能力 | 备注 |
---|---|---|
ShanhaiPower.login("xxx") | 通过用户标识进行登录(xxx为用户名或ID) | 1.默认渠道为Default 2.返回TokenInfo |
ShanhaiPower.login("xxx","PC") | 指定渠道登录 | 返回TokenInfo |
ShanhaiPower.getCurrentUserToken() | 获取当前登录的用户信息 | 返回TokenInfo |
ShanhaiPower.setTokenSessionData(String key,Object data) | 设置基于Token的会话级数据 | |
ShanhaiPower.getTokenSessionData(String key) | 获取基于Token的会话级数据 | |
ShanhaiPower.logOutByToken(String token) | 注销指定Token | |
ShanhaiPower.logOut(Object userFlag) | 注销指定用户会话 | |
ShanhaiPower.logOut(Object userFlag,String channel) | 注销指定用户指定渠道会话 |
忽略用户会话校验
@RequestNotNeedAuth
@GetMapping("/login")
public TokenInfo login(){
return ShanhaiPower.login("xxx");
}
# 1.5 注解权限组件
注解权限组件配置参数如下:
shanhai:
power:
annotation-permissions-enable: true #启用路由权限组件
permission-path-patterns: #权限组件拦截范围 (路由和注解权限共享)
- /**
permission-exclude-path-patterns: #权限组件拦截白名单范围 (路由和注解权限共享)
- /api/xxx
注解权限组件示例如下:
@RequiresPermissions("user:details")
@GetMapping("/queryUserInfo")
public TokenInfo queryUserInfo(){
return ShanhaiPower.getCurrentUserToken();
}
在对应的方法上添加@RequiresPermissions,并且写入权限编码。
实现PermissionService,动态为当前用户追加权限编码。
@Service
public class PermissionServiceImpl implements PermissionService {
@Override
public List<String> queryAllPermission(HttpServletRequest request) {
List<String> allPermission=new ArrayList<>();
allPermission.add("user:details");
allPermission.add("user:route");
return allPermission;
}
}
# 1.6 路由权限组件
路由权限组件配置参数如下:
shanhai:
power:
route-permission-enable: true #启用路由权限组件
route-permissions: #路由权限配置(可以配置多个)
- path: '/route/**'
permission: 'user:route'
permission-path-patterns: #权限组件拦截范围 (路由和注解权限共享)
- /**
permission-exclude-path-patterns: #权限组件拦截白名单范围 (路由和注解权限共享)
- /api/xxx
对于一些简单的系统,可能只想基于路由做一些简单的控制,此时可以考虑使用路由组件。
需要注意的是,路由组件支持通过配置文件进行配置
也支持通过实现PermissionService中的loadRoutePermissionConfig方法来进行。自定义加载的方式优先级高于配置文件的方式。
路由权限组件同样需要实现PermissionService,动态为当前用户追加权限编码。
实现方式参考第1.5章节。
# 1.7 用户安全锁定组件
在配置文件中新增如下配置参数:
shanhai:
power:
lockThreshold: 5 #锁定阈值(默认:5)
lockThresholdExpire: 3600 #锁定阈值累计有效期(单位:s,默认:1小时)
lockExpire: 1800 # 锁定时长(单位:s,默认:30分钟)
安全锁定组件相关方法如下:
API说明 | API能力 | 备注 |
---|---|---|
ShanhaiPower.loginLock(Object userFlag) | 登录锁定判断 | 指定用户是否已被锁定,返回Boolean类型 |
ShanhaiPower.loginLock(Object userFlag,String channel) | 登录锁定判断 | 指定用户&指定登录渠道,是否已被锁定,返回Boolean类型 |
ShanhaiPower.loginFailure(Object userFlag) | 登录失败调用 | 当用户登录失败时,手动调用触发阈值累计方法 |
ShanhaiPower.loginFailure(Object userFlag,String channel) | 登录失败调用 | 当用户指定渠道登录失败时,手动调用触发阈值累计方法 |
# 1.8 统一异常管理
组件异常类 | 说明 |
---|---|
ShanHaiNotLoginException | 用户会话鉴权相关异常 |
ShanHaiNotPermissionException | 用户权限相关异常 |
ShanHaiPowerException | 异常基类 |
可以使用SpringBoot的统一异常管理进行控制
@ExceptionHandler(value = ShanHaiPowerException.class)
public ResponseEntity<?> shanHaiPowerErrorHandler(Exception e) {
Map<String, Object> resp=new HashMap<>();
resp.put("code",((ShanHaiPowerException)e).getCode());
resp.put("message",e.getMessage());
HttpHeaders headers = new HttpHeaders();
MediaType mediaType = new MediaType("application","json", StandardCharsets.UTF_8);
headers.setContentType(mediaType);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).headers(headers).body(resp);
}
# 1.9 扩展-会话持久化
可以通过实现PowerStoreService,来实现自定义的会话持久化。
/**
* 会话存储服务
* @author Shmily
*/
public interface PowerStoreService {
/**
* 设置缓存失效时间
* @param key
* @param time (单位s)
* @return
*/
Long expire(String key, int time);
/**
* 判断key是否存在
* @param key
* @return
*/
boolean exists(String key);
/**
* 查询key过期时间
* @param key
* @return
*/
long ttl(String key);
/**
* 删除key
* @param key
* @return
*/
void del(String key);
/**
* 读取key对应的值
* @param key
* @return
*/
Object get(String key);
/**
* 设置key:value
* @param key
* @return
*/
void set(String key, Object value);
/**
* 设置key和过期时间
* @param key
* @return
*/
void set(String key, Object value, long time);
}
# 1.10 扩展-Token生成规则
组件默认集成了uuid和sha512方式的Token生成规则,可以通过配置参数来实现。
同时,也支持自己实现TokenGenerateService,来实现自定义的Token生成规则。
如果需要传入额外参数生成Token,需要在登录的时候使用如下方法登录:
ShanhaiPower.login(Object userFlag,String channel, Map<String, Object> extParams)
# 2.OTP双因素认证扩展(MFA)
山海power集成了HOTP和TOTP,使用时需要引入如下依赖:
<properties>
<commons-codec.version>1.15</commons-codec.version>
<zxing-core.version>3.3.3</zxing-core.version>
<zxing-javase.version>3.3.3</zxing-javase.version>
</properties>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing-core.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${zxing-javase.version}</version>
</dependency>
用户和OTP集成方式因人而异,因此需要自行绑定用户身份和OTP密钥并做好存储。
推荐使用TOTP作为双因素认证使用,HOTP的counter容易被爆破,从而降低安全性。
# 2.1 HOTP示例
String qrCodeContent=hotp.getURI(1111,"testuser").toString();
//自动生成密钥
byte[] secret = SecretGenerator.generate();
//偏移值,可以自行定义
long counter=7891;
HOTPGenerator hotp = new HOTPGenerator.Builder(secret)
.withPasswordLength(6)
.withAlgorithm(HMACAlgorithm.SHA256)
.build();
//生成OTP协议(可以用于生成二维码)
String qrCodeContent=hotp.getURI(counter,"testuser").toString();
//校验动态码是否正确
hotp.verify("123456",counter);
# 2.2 TOTP示例
// Generate a secret (or use your own secret)
byte[] secret = SecretGenerator.generate();
TOTPGenerator totp = new TOTPGenerator.Builder(secret)
.withHOTPGenerator(builder -> {
builder.withPasswordLength(6);
builder.withAlgorithm(HMACAlgorithm.SHA256); // SHA256 and SHA512 are also supported
})
.withPeriod(Duration.ofSeconds(30))
.build();
//生成OTP协议(可以用于生成二维码)
String qrCodeContent=totp.getURI("platName","zhangsan").toString();
//校验动态码是否正确
totp.verify("331197")
# 2.3 生成OTP绑定二维码
int width = 800;// 二维码宽度
int height = 800;// 二维码高度
int margin = 10;// 二维码边距
String logoPath = "d:/xxx.jpg";// LOGO图片路径
int logoSizeMultiple = 3;// 二维码与LOGO的大小比例
String filePath = "d:/otp.jpg";// 指定生成图片文件的保存路径
String fileName = "totp";// 生成的图片文件名
String formatName = "jpg";// 生成的图片格式,可自定义
try {
// 生成二维码(qrCodeContent即为2.1或2.2中生成的内容)
BufferedImage qrcode = QRCodeUtils.createQRCode(qrCodeContent, width, height,margin);
// 添加LOGO
// qrcode = QRCodeUtils.createQRCodeWithLogo(qrcode, width, height, logoPath,logoSizeMultiple);
// 导出到指定路径
boolean result = QRCodeUtils.generateQRCodeToPath(qrcode, filePath, fileName, formatName);
System.out.println("执行结果" + result);
} catch (Exception e) {
e.printStackTrace();
}
可以使用支持OTP协议的客户端进行扫码,如2FAS