在Shrio 中有俩种默认Realm.
一、IniRealm
在resources目录新建 user.ini ,其中,users是写的用户名和密码,以便进行认证。授权则是roles下设置的权限,可以不写。
在案例中代表:一个admin用户,密码是123456,角色是admin,拥有的权限是删除、修改。
[users]
admin=123456,admin
[roles]
admin=user:delete,user:update
测试代码如下
private DefaultSecurityManager defaultSecurityManager;
@Test
public void testAuthentication() {
//1.构建SecurityManager环境
defaultSecurityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:user.ini");
defaultSecurityManager.setRealm(iniRealm);
//2.由主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); //设置manager环境
Subject subject = SecurityUtils.getSubject(); //获取主体
//默认构造器,构建要验证的测试数据
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
subject.login(token); //提交认证登陆
//3.1 登陆认证
System.out.println("认证结果:" + subject.isAuthenticated());
//System.out.println("授权:");
subject.checkRole("admin"); //验证角色
//subject.checkPermission("user:delete");
//subject.checkPermission("user:insert"); //没有这个权限
//3.2 退出
subject.logout();
System.out.println("退出后,认证结果:" + subject.isAuthenticated());
}
认证成功、授权成功
认证失败(写个错误账号)
授权失败(写个该用户没有的角色或权限)
权限异常
二、JdbcRealm
代码类似IniRealm。
添加俩个依赖包
<!--数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!--mysql-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
在这里,我们无需写sql查询,是因为JdbcRealm 已经封装好了,在JdbcRealm 代码中可以看一下其相关Sql,所以对照,可以创建表,来存储授权相关信息
数据表如下
用户表:保存用户名和密码
CREATE TABLE `users` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
角色表:保存用户名和角色
CREATE TABLE `user_roles` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`role_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
权限表:保存角色和权限
CREATE TABLE `roles_permissions` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`role_name` varchar(255) DEFAULT NULL,
`permission` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
测试代码如下
private DefaultSecurityManager defaultSecurityManager;
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
}
@Test
public void testAuthentication(){
//1.构建SecurityManager环境
defaultSecurityManager = new DefaultSecurityManager();
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource); //设置数据源
jdbcRealm.setPermissionsLookupEnabled(true); // 默认false,打开权限查询
defaultSecurityManager.setRealm(jdbcRealm);
//2.由主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager); //设置manager环境
Subject subject = SecurityUtils.getSubject(); //获取主体
//默认构造器,构建要验证的测试数据
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
subject.login(token ); //提交认证登陆
//3.1 登陆认证
subject.checkRole("admin");
System.out.println("认证结果:"+subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermission("user:select");
//3.2 退出
subject.logout();
System.out.println("退出后,认证结果:"+subject.isAuthenticated());
}
测试,如同IniRealm。
如果不想用默认的数据库,也可以用自己创建的数据库.
数据库设置一样,只是要自己写SQL
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
}
private final String USER_SQL = "select password from test_user where username=?";
private final String ROLE_SQL = "select role_name from test_user_roles where username=?";
创建好jdbcRealm后,使用自己的数据库,就把相关SQL写入。
jdbcRealm.setAuthenticationQuery(USER_SQL); //认证
jdbcRealm.setUserRolesQuery(ROLE_SQL); //验证角色
jdbcRealm.setPermissionsQuery(...);;//验证拥有的权限
三、自定义Realm
通过上面的JDBCRealm,可以知道,继承AuthorizingRealm即可,重写其俩个抽象方法即可。在使用时,给DefaultSecurityManager设置Realm时用自定义的。
/**
* 自定义dbcRealm
*/
public class CustomRealm extends AuthorizingRealm {
{
super.setName("customName");
}
/**
* 授权
主体* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从缓存或数据库中拿到用户名
String username = (String) principalCollection.getPrimaryPrincipal();
//从数据库或者缓存中通过用户名拿到角色
Set<String> roles = getRolesByUsername(username);
//从数据库或者缓存中拿到权限
Set<String> permissions = getPermissionsByUsername(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo() ;
authorizationInfo.setRoles(roles); //设置角色
authorizationInfo.setStringPermissions(permissions); //设置权限
return authorizationInfo;
}
/**
* 认证
* @param authenticationToken : 主体传过来的认证信息
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.从缓存或数据库(认证体)中获得用户名
String username = (String) authenticationToken.getPrincipal();
//2.通过获获得用户名在数据库中进行校验
String password = getPasswordByUsername(username);
if(password == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("admin",password,"customName");
return authenticationInfo;
}
}
使用时:
@Test
public void testAuthentication() {
//1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
CustomRealm customRealm = new CustomRealm();
defaultSecurityManager.setRealm(customRealm);
... ...
}
验证结果可以仿照例1.
在这里插入代码片
四、Shiro加密
在测试类中,添加加密,然后就加密设置到customRealm中
//设置Shiro加密
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//设置加密方式
matcher.setHashIterations(1); //设置加密次数
customRealm.setCredentialsMatcher(matcher);
另外,为了更加安全,可以用‘加盐’,在用户密码存储时就是用加盐存储的,所以只需要修改customRealm.java,加盐
//加盐,为了不被破解,“admin”是自定义
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("admin"));
综合以上俩个加密,可以理解成:
Md5Hash md5Hash = new Md5Hash(‘密码’,"admin");