一、RBAC模型
RBAC模型(Role-Based Access Control:基于角色的访问控制)
组成:用户、角色和权限
RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离,最终可以映射以下关系:
-
User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
-
Role(角色):不同角色具有不同的权限
-
Permission(权限):访问权限
-
用户-角色映射:用户和角色之间的映射关系
-
角色-权限映射:角色和权限之间的映射
二、Apache Shiro
Java 安全框架 ,它执行身份验证、授权、加密和会话管理
Shiro 提供应用程序安全 API 来执行以下方面(我喜欢将这些称为应用程序安全的 4 个基石):
-
Authentication(认证):用户身份识别,通常被称为用户“登录”
-
Authorization(授权): 访问控制。比如某个用户是否具有某个操作的使用权限。
-
Session Management(会话管理): 特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
-
Cryptography(加密): 在对数据源使用加密算法加密的同时,保证易于使用。
三、应用程序
1.pom.xml文件导入依赖包
shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.0</version>
</dependency>
EhCache依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.5.3</version>
</dependency>
2.创建 mybatis-config.xml 配置文件(链接数据库)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/rbac?characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/SysUserMapper.xml"/>
</mappers>
</configuration>
创建ehcache.xml文件(缓存)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache >
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
3.建立各层
eg:
service层:
package com.wn.rbac.service.impl;
import com.wn.rbac.entity.SysUser;
import com.wn.rbac.mapper.SysUserMapper;
import com.wn.rbac.service.UserService;
import com.wn.rbac.util.DBTools;
import org.apache.ibatis.session.SqlSession;
import java.util.Set;
/**
* @author :fengSir
* @date :Created By 2022-06-12 15:18
* @description :TODO
*/
public class UserServiceImpl implements UserService {
@Override
public SysUser getUserByTelephone(String telephone) {
SqlSession sqlSession = DBTools.getSession();
SysUserMapper userMapper = sqlSession.getMapper(SysUserMapper.class);
SysUser user = userMapper.selectUserByPwd(telephone);
sqlSession.commit();
return user;
}
@Override
public Set<String> getRolesByTelephone(String telephone) {
SqlSession sqlSession = DBTools.getSession();
SysUserMapper userMapper = sqlSession.getMapper(SysUserMapper.class);
Set<String> set = userMapper.selectRolesByTelphone(telephone);
sqlSession.commit();
return set;
}
@Override
public Set<String> getPersByTelephone(String telephone) {
SqlSession sqlSession = DBTools.getSession();
SysUserMapper userMapper = sqlSession.getMapper(SysUserMapper.class);
Set<String> set = userMapper.selectPersByTelephone(telephone);
sqlSession.commit();
return set;
}
public static void main(String[] args) {
System.out.println(new UserServiceImpl().getRolesByTelephone("13892845500"));
}
}
工具类:
package com.wn.rbac.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class DBTools {
private static SqlSessionFactory sqlSessionFactory;
static{
String resource = "mybatis-config.xml";
InputStream inputStream=null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
4.自定义Realm
package com.wnxy.myrealm.util;
import com.wnxy.myrealm.entity.SysUser;
import com.wnxy.myrealm.service.SysUserService;
import com.wnxy.myrealm.service.impl.SysUserServiceImpl;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.Set;
/**
* @author Mrz
* @date 2022/8/15 18:55
*/
public class MyRealm extends AuthorizingRealm {
private SysUserService service = new SysUserServiceImpl();
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("负责验权");
//获取tel
String principal = (String) principalCollection.getPrimaryPrincipal();
//访问后台数据库查询出对应的角色和权限
Set<String> strings = service.selectRolesByTel(principal);
Set<String> strings1 = service.selectPersByTel(principal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(strings);
simpleAuthorizationInfo.setStringPermissions(strings1);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("负责认证");
String tel = (String)authenticationToken.getPrincipal();
SysUser sysUser = service.selectSysUserByTel(tel);
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(tel, sysUser.getPassword(), getName());
//解盐(在已加入的算法“MD5”和迭代次数1024的基础上,加盐,最后得出用户输入的密码的加密结果,和数据库里的加密结果进行比对,最终返回true或false)
simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(tel));
return simpleAuthenticationInfo;
}
}
5.测试(Shiro执行流程(包括加密和缓存))
@Test
void testShiro(){
//缓存配置
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
//认证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashIterations(1024);
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//1、得到安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//绑定缓存
defaultSecurityManager.setCacheManager(ehCacheManager);
//2、得到领域
MyRealm myRealm = new MyRealm();
//绑定认证匹配器
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
//3、将领域绑定到securityManager
defaultSecurityManager.setRealm(myRealm);
//4、得到安全管理工具
SecurityUtils.setSecurityManager(defaultSecurityManager);
//5、得到主体
Subject subject = SecurityUtils.getSubject();
//6、得到验证信息
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("13892845500", "123456");
//验证
try {
subject.login(usernamePasswordToken);
log.info("是否拥有管理角色:{}",subject.hasRole("管理员"));
log.info("是否拥有权限管理权限:{}",subject.isPermitted("权限管理"));
log.info("登录成功");
}catch (UnknownAccountException uae) {
log.info("未知账号");
}catch (IncorrectCredentialsException ice) {
log.info("密码错误");
}catch (LockedAccountException lae){
log.info("账户被锁");
}catch (AuthenticationException e) {
log.info("未知异常");
}
}
(显示加密结果)
@Test
void testMD5(){
SimpleHash md5 = new SimpleHash("MD5", "123456", "13892845500",1024);
log.info(md5.toHex());
}