网站首页 > 精选教程 正文
一、概述
java应用系统设计过程中,用户认证、用户授权、鉴权是绕不过去的话题。
如果这个权限管理的设计,没有做到与业务系统的隔离,拓展性不够强,很容易就会拖后腿。
这个问题应该做过开发的同学都会有所体会。
现在网络上的各种关于权限管理的框架比较主流的有 Apache Shiro,Spring Security,Sa-Token(新兴起的一个优秀框架)。
这里会有同学说,既然已经有这么多的成熟优秀的权限管理框架,为什么还有再给大家介绍这种实现思路。
在本人工作和学习的过程中,经常会使用这些优秀的权限管理框架。
但是,一旦是这些三方框架出现的异常和问题,想要排查,就比较麻烦。要么就是靠着百度大家的经验。要么就是猛扒代码,一点点去排查。
三方框架对于我们使用者来说,就像是一个黑盒。这一点一直让我觉得有点不顺畅。
同学们,谁不想要一个自己知根知底的的权限管理框架呢。
二、框架使用体验
2.1 项目初始化配置
Springboot老三样。
- 添加pom依赖
- 修改配置文件
- 编写组件代码
引入pom依赖:
<!-- 引入鉴权框架 客户端-->
<dependency>
<groupId>com.lhit.security</groupId>
<artifactId>security-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 引入鉴权框架 服务端-->
<dependency>
<groupId>com.lhit.security</groupId>
<artifactId>security-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
修改配置文件:
lhit:
security:
server-url-of-check-url: /security/check_url # 不配置默认就是这个路径
server-url-of-check-perms-code: /security/check_perms_code # 不配置默认就是这个路径
server-url-of-check-static-res-path: /security/check_static_res_path # 不配置默认就是这个路径
server-url-of-token-to-authority: /security/token_to_authority # 不配置默认就是这个路径
useCloud: false # 不配置默认为true回去注册中心查找授权中心服务
token-key: LHTOKEN # 请求头中token的key
expire: 9999999
2.2 用户登录
自定义一个凭证类
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UsernamePasswordVerification implements SecurityVoucher {
// 用户名
@NotBlank(message = "未上传 username")
@ApiModelProperty(value = "username")
private String username;
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码")
private String password;
}
自定义一个凭证类认证器:
这个认证器很简单 就是默认admin 密码 123456 然后给与了固定的角色和全部的资源。实际应用中应该从数据库中获取到用户的权限 并组织返回的securityAuthority。
@Component
public class DefaultUsernamePasswordVoucherVoucherVerification implements SecurityVoucherVerification<UsernamePasswordVerification> {
@Override
public SecurityAuthority verification(UsernamePasswordVerification usernamePasswordVoucher) throws Exception {
if (usernamePasswordVoucher.getUsername().equals("admin") && usernamePasswordVoucher.getPassword().equals("123456")) {
SecurityAuthority securityAuthority = new SecurityAuthority();
securityAuthority.setSecurityUser(new SecurityUser("1", "admin"));
securityAuthority.setSecurityRoleList(Lists.newArrayList(new SecurityRole(0L,"roleNo","管理员")));
securityAuthority.setSecurityResList(Lists.newArrayList(SecurityRes.allCodeRes(), SecurityRes.allUrlRes()));
return securityAuthority;
}
throw CommonException.create(ServerResponse.createByError("用户名或密码错误,默认admin-123456"));
}
}
开放认证接口:
@Slf4j
@Api(tags = "认证接口")
@RestController
@RequestMapping("/security")
public class AuthorizeController {
@Autowired
private SecurityServer securityServer;
@PostMapping("/login/username_password")
@ApiOperation("系统用户登录")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "body", dataType = "UsernamePasswordVerification", dataTypeClass = UsernamePasswordVerification.class, name = "param", value = "参数")
})
public ServerResponse<AuthenticationVo> sysUserLoginByUsernamePassword(@Validated @RequestBody UsernamePasswordVerification param) throws Exception {
log.info("|-----------------------------------------------|");
log.info("进入 系统用户登录 接口 : LoginAuthenticationController-sysUserLoginByUsernamePassword ");
AuthenticationVo authorize = securityServer.authorize(param);
return ServerResponse.createBySuccess("登录成功", authorize);
}
@GetMapping("/logout")
@ApiOperation("用户退出")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "header", dataType = "string", name = "LHTOKEN", value = "用户token"),
})
public ServerResponse logout(@RequestHeader(value = "LHTOKEN", defaultValue = "") String token) throws Exception {
log.info("|-----------------------------------------------|");
log.info("进入 获取当前用户信息 接口 : SysUserController-logout");
securityServer.tokenDestroy(new TokenParam(token));
return ServerResponse.createBySuccess("退出成功");
}
/**
* 获取当前用户信息
*/
@GetMapping("/current_user_prems")
@ApiOperation("获取当前用户权限信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "header", dataType = "string", name = "LHTOKEN", value = "用户token"),
})
@TokenToAuthority // 这个注解将请求同中的token信息转换为securityAuthority参数,到当前方法中。
public ServerResponse<SecurityAuthority> getUesrPremsInfo(@ApiIgnore SecurityAuthority securityAuthority) throws Exception {
log.info("|-----------------------------------------------|");
log.info("进入 获取当前用户信息 接口 : SysUserCurrentUserController-getUesrInfo");
return ServerResponse.createBySuccess("获取成功", securityAuthority);
}
}
2.3 权限验证
路由级别鉴权:
不用做其他额外的配置 只需要打上@HasUrl 就会获取到Controller层的当前url地址,并校验用户是否有访问该url的权限。
并将解析后的用户信息放到方法的SecurityAuthority参数中
在第一步用户登录时,默认给了SecurityRes.allUrlRes() ,则配置了 /** 的url访问权限。
/**
* 删除用户
*/
@DeleteMapping("/delete/{userId}")
@ApiOperation("删除用户")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "header", dataType = "string", name = "LHTOKEN", value = "用户token"),
@ApiImplicitParam(paramType = "path", dataType = "Long", dataTypeClass = Long.class, name = "userId", value = "用户id")
})
@HasUrl
public ServerResponse delUser(@PathVariable(value = "userId") Long userId, @ApiIgnore SecurityAuthority securityAuthority) throws Exception {
log.info("|-----------------------------------------------|");
log.info("进入 删除用户 接口 : SysUserAdminController-delUser");
sysUserService.delUser(userId, getCurrentSysUser(securityAuthority));
return ServerResponse.createBySuccess("删除成功");
}
方法级别鉴权
@Slf4j
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
/**
* 删除用户
*/
@Override
@Transactional(rollbackFor = Exception.class)
@HasPermsCode(permsCode = "user:delete") // 该数据会校验springMVC上下文中token是否有访问该资源的权限
public void delUser(Long userId, SysUser sysUser) throws Exception {
log.info("开始 删除用户");
SysUser checkUser = sysUserDao.getById(userId);
if (checkUser == null) {
throw CommonException.create(ServerResponse.createByError("用户信息不存在"));
}
try {
// 删除用户
checkUser.setDelFlag(true);
checkUser.setUpdateBy(sysUser.getId());
checkUser.setUpdateTime(new Date());
sysUserDao.updateById(checkUser);
log.info("完成 删除用户");
} catch (Exception e) {
throw CommonException.create(e, ServerResponse.createByError("删除用户失败,请联系管理员"));
}
}
}
验证用户是否登录
@Slf4j
@Api(tags = "认证接口")
@RestController
@RequestMapping("/security")
public class AuthorizeController {
/**
* 获取当前用户信息
*/
@GetMapping("/current_user_prems")
@ApiOperation("获取当前用户权限信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "header", dataType = "string", name = "LHTOKEN", value = "用户token"),
})
@TokenToAuthority // 这个注解将请求同中的token信息转换为securityAuthority参数,到当前方法中。如果转换失败抛出401异常
public ServerResponse<SecurityAuthority> getUesrPremsInfo(@ApiIgnore SecurityAuthority securityAuthority) throws Exception {
log.info("|-----------------------------------------------|");
log.info("进入 获取当前用户信息 接口 : SysUserCurrentUserController-getUesrInfo");
return ServerResponse.createBySuccess("获取成功", securityAuthority);
}
}
三、时间地点人物
想要描述一个事情,都是将时间地点人物介绍完,才能吧事情描述清楚。
介绍这个设计思路也需要介绍前提:
- 什么时候用这个框架
- 框架能提供哪些能力
- 框架应该有哪些抽象组件
3.1 什么时候用这个框架
显然,如果系统需要提供用户认证、用户授权、用户鉴权的时候,就需要有一个权限管理的模块。
整个流程应该是:
用户认证 --> 颁发token(用户授权) --> 用户鉴权 --> token回收
3.2 框架要提供哪些能力
- 首先可以对系统用户进行认证。
- 可以将生成用户口令给客户端,并可以管理该口令。
- 用户携带口令访问资源是可以判断用户是否有权限来访问这个资源。
以上能力老生常谈就是最基础的权限管理。
3.3 框架应该有哪些抽象组件
这个问题是面向对象开发的java程序员必须要好好思考的问题,就是当你接到一个需求时,如何以面向对象的思维来分析和设计程序来完成需求。
3.3.1 用户认证
用户认证,最最常见的场景就是用户名密码登录。
在这个场景中可能存在:
用户名+密码、用户名+密码+验证码、手机号+验证码、邮箱+验证码 ...... 这么多的登录方式。
而通常来验证这些登录信息是否合法,一般都是要去数据库中读取用户的注册信息来完成认证。
这个场景下可以抽象出来的类有:
1. 凭证类:用户名+密码、用户名+密码+验证码、手机号+验证码、邮箱+验证码 ......
2. 凭证类验证器:用来验证用户上传的凭证是否是合法的。
3.3.2 用户授权
当用户完成认证凭证验证后,服务器应该返回一个用户的口令(token),给用户使用。
并且用户的token应该可以关联并携带出用户绑定的所有资源权限,和角色、部门、岗位等等信息。
用户的资源又分为:
静态资源:
菜单、按钮等静态资源
文档、图片等静态资源
动态资源:
对某种资源的CURD权限:如 是否可以对 sys_user表数据进行CURD。
这个场景下可以抽象出来的类:
- Token生成器:用来生成用户token.
- 用户权限描述类:存放用户基本信息、用户拥有的资源权限、用户的角色信息、用 户的其他信息(例如:岗位、部门等)
- 角色:典型的RBAC设计,角色代表权限的集合。
- 岗位:从另外一种维度给用户打标签,来区分用户的权限。
- 部门:从另外一种维度给用户打标签,来区分用户的权限。
其中的岗位和部门,有些权限管理框架中没有,有的或许有一个,这里不纠结这个问题,无论是部门还是岗位,其实都是提供了一种权限判断的维度,类型给用户打上一种标签。
3.3.3 token管理
生成用户token后,所有的token需要管理起来。可以用来统计和维护。
所以需要将上一步获取到的用户权限描述类的信息与token建立一种映射关系。从而可以通过token获取到用户的各种信息。
这个场景可以抽象出来的类:
Token管理类:用来管理所有生成的token。并建立用户信息与token的关联关系。
3.3.4 用户鉴权
当用户通过用户认证和用户授权后,就获取到了他的token口令。
每次用户来访问服务资源时,都需要携带token,当服务器收到请求后,需要通过token获取到用户的所有的权限信息,来判断用户是否可以访问当前资源。
这个场景似乎没有可以抽离出来的类,而是我们要找到一种用户鉴权的方案。
这里,根据以往的经验,基于Spring的AOP切面编程应该是对使用者最友好的方式
所以这里总结下我们需要鉴权的类型:
- 用户身份验证:token是否对应着一个有效的用户身份。就是用户是不是已经登录。
- URL基本的鉴权:对于java开发来说就是Controller层开放的接口释放可访问。
- 方法级别鉴权:对于java开发者来说,就对应着某个类的某个方法该用户是否可以访问,这里可以参考shiro的授权码 user:delete表示是否可以删除用户。
- 静态资源鉴权:用户是否可以访问系统中的静态资源。比如 一张图片的下载地址。
四、小结
上面铺垫了那么些,其实只是想让大家能跟笔者有一个相同的认知。
- 用户认证:就是用户登录。
- 用户授权:就是为用户颁发一个可以表达他拥有的角色和自有的口令。
- 用户鉴权:就是判断用户有没有权限来访问当前的资源。
先梳理下上面总结出来的类。
凭证类、凭证类验证器、token生成器、token管理器。
以及,基于AOP实现的用户鉴权方案。
大致思路:
未完待续。。。
敬请关注下一篇
猜你喜欢
- 2024-11-26 关于大后台的权限管理设计
- 2024-11-26 基于Springboot的权限管理系统
- 2024-11-26 Sa-Token,让你的权限认证更简单
- 2024-11-26 Sa-Token之注解鉴权:优雅的将鉴权与业务代码分离
- 2024-11-26 Shiro 权限校验分析
- 2024-11-26 基于SpringBoot 的MCMS系统,完全开源,直接商用太爽了
- 2024-11-26 再见前端!纯 Java 撸个后台管理系统,这框架用起来贼爽
- 2024-11-26 AOP编程_Android优雅权限框架(2)Demo完全解析
- 2024-11-26 6个顶级SpringCloud微服务开源项目,企业开发必备
- 2024-11-26 什么是Java框架?刚接触Java的初学者该如何去学习框架呢?
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)