主题
认证授权
底座管理系统实现了登录认证及后端接口的权限控制等功能。支持轻量级Filter
和SpringSecurity
两种模式。具体组件坐标如下:
xml
<!-- 认证及授权相关controller接口及web配置 -->
<dependency>
<groupId>cn.zjtele.pubinfo.sys.auth</groupId>
<artifactId>auth-api</artifactId>
</dependency>
<!-- 认证及授权相关服务及扩展定义 -->
<dependency>
<groupId>cn.zjtele.pubinfo.sys.auth</groupId>
<artifactId>auth-core</artifactId>
</dependency>
<!-- 基于底座rbac的相关服务扩展实现 -->
<dependency>
<groupId>cn.zjtele.pubinfo.sys.auth</groupId>
<artifactId>auth-rbac</artifactId>
</dependency>
认证鉴权流程
认证鉴权鉴权模式
底座管理系统支持支持轻量级Filter
和SpringSecurity
两种认证鉴权模式。
轻量级Filter模式
默认情况,系统使用轻量级Filter模式,认证过滤器cn.zjtele.pubinfo.sys.auth.filter.AuthenticationFilter
及鉴权过滤器cn.zjtele.pubinfo.sys.auth.filter.AuthorizationFilter
进行登录认证及后端接口权限校验。
SpringSecurity模式
如果引入了SpringSecurity的maven依赖,自动切换为SpringSecurity模式。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
接口鉴权逻辑
如果需要针对某个后端接口进行权限校验,需要在资源管理
页面,对资源进行编辑,配置资源对应的权限标识,权限标识为后端接口地址(不包含contextPath),多个以逗号分隔。这样如果用户拥有该资源的权限,则拥有该后端接口的访问权限。没有在资源中配置的其他接口,则不进行接口权限校验。具体页面配置如下:
配置介绍
登录认证、授权校验,密码等相关配置
yaml
pubinfo:
# 认证授权相关配置
auth:
# 登录后访问token相关配置
access-token:
# 访问token的过期时间
expire-time: 1800000
# 访问token的生成签名key
key: UT@33oUY*
# 登录后刷新token相关配置
refresh-token:
# 刷新token的生成签名key
key: OK@213YU#
# 刷新token的过期时间
expire-time: 36000000
# 登录认证相关配置
authentication:
# 登录认证开关
enable: true
# 登录认证白名单url列表
white-path: /test/**
# 登录授权相关配置
authorization:
# 登录授权校验开关
enable: true
# 接口授权校验白名单,白名单内接口所有用户都可访问
white-path: /auth/changeLoginOrg,/dict/item/list,/rbac/user/userEntireInfo,/rbac/resource/grantedResources,/rbac/user/changePassword
# 登录锁定配置
login-lock:
# 登录锁定开关
enable: true
# 允许失败次数
allow-fail-counts: 5
# 锁定时间,单位分钟
lock-minutes: 5
# 验证码开关
valid-code-enable: true
rsa:
# 登录密码传输公钥
public-key: xxx
# 登录密码传输私钥
private-key: xxx
默认密码、密码有效期及密码校验规则配置
yaml
pubinfo:
rbac:
password:
# 默认密码
default-password: xxxxxx
# 密码有效期
expire-days: 180
# 是否强制修改初始密码
force-change-init-pwd: true
# 密码不能重复次数
num-of-recent-no-repeat: 2
# 密码校验器
strength-checker-list: contentRegex,length,keyBoard,oldPassword,userName
由于密码传输的加解密相关代码在auth-core组件中,如果其他业务模块中也有需要加密传输密码的需要,同时为了避免模块间的依赖,可以通过切面方式统一解密密码,具体配置如下
yaml
pubinfo:
rsa-decrypt:
# 密码解密切点列表
point-cuts:
# 切点表达式
- expression: execution (* cn.zjtele.pubinfo.sys.rbac.core.service.PubPwdChangeLogService.changePassword(..))
# 需要解密的参数列表,值为springel表达式
param:
- '#root[0]'
- '#root[1]'
登录方式扩展
目前底座只有用户名密码登录方式,但是支持自行扩展新的登录方式,具体扩展步骤如下:
- 1、继承
cn.zjtele.pubinfo.sys.auth.spi.domain.BaseLoginReq
(包含登录方式loginType),定义对应的登录请求参数。 - 2、实现
cn.zjtele.pubinfo.sys.auth.spi.IdentityAuthenticator
接口,完成具体的登录认证逻辑,返回用户信息。其中该接口返回的loginType为登录时传入的参数。 - 3、定义新的登录controller接口,入参为步骤1中定义的参数。
- 4、配置文档,可以在
认证服务
分组下,packages-to-scan加上对应的包路径。
相关实例代码如下:
请求参数实现示例
java
@Schema(name = "QrCodeLoginReq", description = "扫码登录请求对象")
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper=true)
public class QrCodeLoginReq extends BaseLoginReq {
@Schema(description = "二维码", requiredMode = Schema.RequiredMode.REQUIRED)
private String qrCode;
}
认证器实现示例:
java
@Component
public class QrCodeLoginIdentityAuthenticator implements IdentityAuthenticator {
@Override
public UserAccount verify(BaseLoginReq req) {
QrCodeLoginReq loginReq = (QrCodeLoginReq)req;
String userId = getUserIdByCode(loginReq.getQrCode());
if(userId == null) {
throw new BizLogicException("扫码登录异常,用户不存在");
}
UserAccount userAccount = getUserAccountByUserId(userId);
return userAccount;
}
/**
* 根据code获取登录用户信息
* @param code
* @return
*/
private String getUserIdByCode(String code) {
//todo 调用第三方接口获取
return null;
}
private UserAccount getUserAccountByUserId(String userId) {
//todo 调用第三方接口获取
return null;
}
@Override
public String getType() {
return "qrCode";
}
}
controller接口代码示例:
java
@Tag(name = "认证服务")
@RestController
@RequestMapping(AuthConstant.BASE_MAPPING + "/")
public class ExtendLoginController {
@Resource
private LoginService loginService;
@OperationLog(operateType = LogOperatorType.LOGIN)
@Operation(summary = "扫码登录")
@PostMapping("/loginByQrCode")
public ResponseData<LoginTokenVo> loginByQrCode(@RequestBody QrCodeLoginReq req) {
LoginTokenVo loginTokenResp = loginService.login(req);
if (loginTokenResp.getLoginResponse() != null) {
return ResponseData.build(loginTokenResp.getLoginResponse(), loginTokenResp);
}
return ResponseData.success(loginTokenResp);
}
}