开发001

权限管理系统

RBAC基于角色的访问控制

RBAC认为权限授权的过程可以抽象地概括为:Who是否可以对What进行How的访问操作

在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限。
RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离(区别于ACL模型),极大地方便了权限的管理

User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
Role(角色):不同角色具有不同的权限
Permission(权限):访问权限
用户-角色映射:用户和角色之间的映射关系
角色-权限映射:角色和权限之间的映射

RBAC支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则
最小权限原则:RBAC可以将角色配置成其完成任务所需的最小权限集合
责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务,例如要求一个计账员和财务管理员共同参与统一过账操作
数据抽象原则:可以通过权限的抽象来体现,例如财务操作用借款、存款等抽象权限,而不是使用典型的读、写、执行权限

优点:
简化了用户和权限的关系
易扩展、易维护
缺点:
RBAC模型没有提供操作顺序的控制机制,这一缺陷使得RBAC模型很难适应哪些对操作次序有严格要求的系统

RBAC的3种模型
RBAC0,是最简单、最原始的实现方式,也是其他RBAC模型的基础。

RBAC1基于RBAC0模型,引入了角色间的继承关系,即角色上有了上下级的区别。

RBAC2基于RBAC0模型的基础上,进行了角色的访问控制。

员工管理
以列表形式展示账号、工号、姓名、性别、角色、电话、创建时间等信息。通过账号查询实现搜索、添加功能。
create table if not exists tb_emp(
id bigint primary key auto_increment,
username varchar(32) not null unique,
password varchar(32) not null,
realname varchar(20) not null,
sex boolean default 1,
phone varchar(11),
create_time datetime,
update_time timestamp default current_timestamp
)engine=innodb default charset utf8;

员工和角色之间是多对多关联,则需要引入中间表

具体的开发中实际上还应该将tb_emp分为2个表,一个表中记录雇员基本信息,一个是登录表,其中记录登录所需要的信息。
两个表之间采用1对1关联

角色管理
对用户的角色定位及拥有的如添加、修改、删除等权限进行管理。

create table if not exists tb_roles()engine=innodb default charset utf8;

SSM应用开发:
1、添加依赖spring-boot-starter-web、mybatis-spring-boot-starter、druid-spring-boot-starter、
mysql-connector-java、pagehelper-spring-boot-starter
2、创建数据库表
3、使用MybatisX插件进行反向映射,并对方向映射生成的映射元文件、实体类、映射接口进行调整
4、配置 application.yml
spring:
application:
name: back
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
username: root
password: 123456
druid:
max-active: 10
min-idle: 3
max-wait: 2000
server:
port: 6602

mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
com.yan: debug

pagehelper:
helper-dialect: mysql
reasonable: true

5、主类或者对应的配置类上添加注解打开自动扫描配置
@MapperScan(“com.yan.mapper”)

6、添加业务层
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@Service
public class EmpServImpl implements IEmpServ{
}

7、添加控制器以提供外部访问接口API
针对API的访问尽可能遵循RESTful标准
URI: /emps
GET查询、POST创建、PUT修改、DELETE删除
返回数据采用JSON格式

为了减少反复的重复启动测试的过程,可以使用devtools以支持热启动,同时devtools中包含了一个liveServer
支持浏览器在加载重新启动完毕后自动刷新页面显示

直接存储用户名称和对应的登录口令,则可能会有廉政危机问题,也有可能口令大面积泄露的风险

加密算法
加密算法可以归结为三大类:哈希算法、对称加密算法、非对称加密算法

1、DES算法进行加密解密;后来人们觉得DES不够安全,发明了3DES算法;而如今最为流行的对称加密算法是AES算法。
对称算法的好处是加密解密的效率比较高。相应的,对称算法的缺点是不够安全。

2、RSA算法是最常见的非对称加密算法。和对称加密算法比如DES的明显区别在于用于加密、解密的密钥是不同的。使用RSA算法,
只要密钥足够长(一般要求1024bit),加密的信息是不能被破解的。

3、信息摘要算法又叫加密散列算法,加密过程不需要密钥,常见的加密散列算法有MD系列和SHA系列。
从严格意义上来说,哈希算法并不属于加密算法,但它在信息安全领域起到了很重要的作用。最重要的作用就是生成信息摘要,
用以验证原信息的完整性和来源的可靠性。

一个理想的加密散列函数应该具备以下特性:
1、任何信息传入后,输出的总是长度固定;
2、消息摘要看起来是“随机的”,这样根据原始信息就很难推测出值;
3、好的散列函数碰撞概率应该极低,也就是不同信息传入后得到相同值的概率;

解决方案:在数据库中不直接存储口令原文,而是存储口令的密文。密文是通过原文执行消息摘要计算后的结果。可以考虑使用JDK
提供的md5算法进行加密处理,但是由于碰撞算法的发展,所以直接加密的结果可以通过碰撞算法进行解密。

解决方案:引入一个加密算法的参数–salt盐值,而且进行多次hash计算获取加密的指纹数据,可以在一定程度上方法使用碰撞
算法进行解密

JDK提供了md5和sha-1算法的实现,这里选择使用md5进行加密

前后端分离开发或者使用水平扩展部署多个应用时,用户跟踪就是个问题。
1、依赖于代理服务器Nginx实现
2、可以使用spring session依赖于redis实现
3、依赖于token身份验证方式实现

token就是一种身份验证方法,扩展性和安全性更高,非常适合用在Web应用和移动开发应用上。

1、token验证流程
使用token身份验证,服务器端就不会存储用户的登录记录。
(1)客户端使用用户名跟密码请求登录;
(2)服务端收到请求,去验证用户名与密码;
(3)验证成功后,服务端会签发一个Token,再把这个Token发送给客户端;
(4)客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local Storage里;
(5)客户端每次向服务端请求资源的时候需要带着服务端签发的Token;
(6)服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据。

token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:
1、支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后
不会存在信息丢失问题
2、无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服
务端压力
3、更适用CDN:可以通过内容分发网络请求服务端的所有资料
4、更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
5、无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

2、JWT即json web token,是一个开放标准rfc7519,它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象
安全地传输信息。它是以JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程
中还可以完成数据加密、签名等相关处理。

http协议无状态的,所以需要sessionId或token的鉴权机制,jwt的token认证机制不需要在服务端再保留用户的认证信息
或会话信息。这就意味着基于jwt认证机制的应用程序不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利,
jwt更适用于分布式应用

1、前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的
传输(HTTPS),从而避免敏感信息被嗅探
2、后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼
接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
3、后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的
JWT Token即可
4、前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
5、后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等
6、验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

JWT的优势是:
1、简洁:JWT Token数据量小,传输速度也很快
2、因为JWT Token是以JSON加密形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持
3、不需要在服务端保存会话信息,也就是说不依赖于cookie和session,所以没有了传统session认证的弊端,特别适用于
分布式微服务
4、单点登录友好:使用Session进行身份认证的话,由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证
的话, token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会存在这些问题
5、适合移动端应用:使用Session进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到Cookie(需要
Cookie 保存 SessionId),所以不适合移动端

3、jwt的组成部分
标准的jwt令牌分为三部分,分别是Header、payload、signature;在token字符串中使用.进行分割

3.1、Header
它的组成部分包括两点:参数类型jwt,签名的算法hs256

3.2、Payload
它的组成就是登陆用户的一些信息,和token发行和失效时间等;这些内容里面有一些是标准字段,你也可以添加其它需要的内容。
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
最后它也会通过Base64加密方式进行编码

5、Signature
是由3个部分组成,先是用Base64编码的header和payload,再用加密算法加密一下,加密的时候要放进去一个Secret,
这个相当于是一个密码,这个密码秘密地存储在服务端。

secret就是在最后第二次加密时加的盐,算是一个秘钥(只保留在服务器),不向外部透露。

JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据。
signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的
加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一
致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,
secretKey实际上代表的是盐值

// 指定token过期时间为10秒
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);

    String token = JWT.create()
            .withHeader(new HashMap<>())  // Header
            .withClaim("userId", 21)  // Payload
            .withClaim("userName", "baobao")
            .withExpiresAt(calendar.getTime())  // 过期时间
            .sign(Algorithm.HMAC256("!34ADAS"));  // 签名用的secret

    System.out.println(token);

解析JWT字符串
// 创建解析对象,使用的算法和secret要与创建token时保持一致
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(“!34ADAS”)).build();
// 解析指定的token
DecodedJWT decodedJWT = jwtVerifier.verify(“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImJhb2JhbyIsImV4cCI6MTU5OTkyMjUyOCwidXNlcklkIjoyMX0.YhA3kh9KZOAb7om1C7o3vBhYp0f61mhQWWOoCrrhqvo”);
// 获取解析后的token中的payload信息
Claim userId = decodedJWT.getClaim(“userId”);
Claim userName = decodedJWT.getClaim(“userName”);
System.out.println(userId.asInt());
System.out.println(userName.asString());
// 输出超时时间
System.out.println(decodedJWT.getExpiresAt());

在实际的SpringBoot项目中一般登录流程:
1、在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token
作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间
2、将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端
3、前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串
4、后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析
出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录

开发流程:
1、依赖Java-jwt
2、定义工具类
//生成jwt token
JWTCreator.Builder builder = JWT.create(); //创建用于生成jwt token的对象
builder.withClaim(“username”,“yanjun”); //添加有效负载,使用token传递的数据
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + 10 * 1000);
builder.withExpiresAt(expireDate); //过期时间,10s后过期
String token=builder.sign(Algorithm.HMAC256(“zhangsan”)); //指定算法用于生成对应的签名,参数就是生成签名时所使用的盐值
System.out.println(token);

//获取token中传递的数据
String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzUxNTc0OTksInVzZXJuYW1lIjoieWFuanVuIn0.cGjegObynlTwixTQNetYSIu1yFjSrjgVfSQNFBKV3vg";
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("zhangsan")).build();
    //会针对token进行验证,如果过期或者签名出错则报异常
    DecodedJWT verify = jwtVerifier.verify(token);

    System.out.println(verify.getHeader());
    System.out.println(verify.getToken());
    System.out.println(verify.getPayload());
    System.out.println(verify.getSignature());

    //从payload中获取传递的数据
    String username = verify.getClaim("username").asString();
    System.out.println(username);
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值