一文总结 Shiro 实战教程

    defaultSecurityManager.setRealm(iniRealm);
    //将安全管理器注入安全工具类 用于获取认证的主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    //获取认证的主体
    Subject subject = SecurityUtils.getSubject();
    //创建令牌
    UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "1234");

    try {
        //认证 通过没有任何的异常
        subject.login(usernamePasswordToken);
        //验证是否通过
        boolean authenticated = subject.isAuthenticated();
        System.out.println("认证通过:"+authenticated);
    } catch (UnknownAccountException e) {
        e.printStackTrace();
        System.out.println("用户名错误!");
    }catch (IncorrectCredentialsException e){
        e.printStackTrace();
        System.out.println("密码错误!");
    }

}

}


* DisabledAccountException(帐号被禁用)
* LockedAccountException(帐号被锁定)
* ExcessiveAttemptsException(登录失败次数过多)
* ExpiredCredentialsException(凭证过期)等




---


#### 4.5 自定义Realm


上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。


###### 1.shiro提供的Realm


![在这里插入图片描述](https://img-blog.csdnimg.cn/a6acaff1809a47cc956b209ff9af4b29.png#pic_center)


###### 2.根据认证源码认证使用的是SimpleAccountRealm


![在这里插入图片描述](https://img-blog.csdnimg.cn/529bfd09ea274db4aace4ebdb6e15297.png#pic_center)


`SimpleAccountRealm的部分源码中有两个方法一个是 认证 一个是 授权`,



public class SimpleAccountRealm extends AuthorizingRealm {
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
SimpleAccount account = getUser(upToken.getUsername());

    if (account != null) {

        if (account.isLocked()) {
            throw new LockedAccountException("Account [" + account + "] is locked.");
        }
        if (account.isCredentialsExpired()) {
            String msg = "The credentials for account [" + account + "] are expired";
            throw new ExpiredCredentialsException(msg);
        }

    }

    return account;
}

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    String username = getUsername(principals);
    USERS_LOCK.readLock().lock();
    try {
        return this.users.get(username);
    } finally {
        USERS_LOCK.readLock().unlock();
    }
}

}


###### 3.自定义realm



/**
* 自定义realm
*/
public class CustomerRealm extends AuthorizingRealm {
//认证方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}

//授权方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String principal = (String) token.getPrincipal();
    if("mosin".equals(principal)){
        return new SimpleAuthenticationInfo(principal,"123",this.getName());
    }
    return null;
}

}


###### 4.使用自定义Realm认证



public class TestAuthenticatorCustomerRealm {
public static void main(String[] args) {
//创建securityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//IniRealm realm = new IniRealm(“classpath:realm.ini”);
//设置为自定义realm获取认证数据
defaultSecurityManager.setRealm(new CustomerRealm());
//将安装工具类中设置默认安全管理器
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//创建token令牌
UsernamePasswordToken token = new UsernamePasswordToken(“mosin”, “1234”);
try {
subject.login(token);//用户登录
System.out.println(“登录成功”);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println(“用户名错误!!”);
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println(“密码错误!!!”);
}

}

}


#### 4.6 使用MD5和Salt



> 
> 实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
> 
> 
> 


###### 1.自定义md5+salt的realm



/**
* 自定义md5+salt realm
*/
public class CustomerMD5Realm extends AuthorizingRealm {

//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    return null;
}
//认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    String principal = (String) token.getPrincipal();
    //根据用户名查询数据库
    if("mosin".equals(principal)){
        // 参数1:用户名 参数2:密码 参数3:盐 参数4:自定义realm的名字
        System.out.println(this.getName());
        return new SimpleAuthenticationInfo(principal, "800d63a19662b2ba95bc2ffa01ab4804", ByteSource.Util.bytes("mosin"),this.getName());
    }
    return null;
}

}


###### 2.使用md5 + salt 认证



public class CustomerMD5RealmTest {

public static void main(String[] args) {

    //创建安全管理器
    DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    //创建自定义MD5Realm对象
    CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
    //创建密码认证匹配器对象
    HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("MD5");
    //设置散列的次数
    md5.setHashIterations(1024);
    //设置密码认证匹配器对象
    customerMD5Realm.setCredentialsMatcher(md5);
    //设置安全管理器的 认证安全数据源
    defaultSecurityManager.setRealm(customerMD5Realm);
    //设置安全工具类的安全管理器
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    //获取认证的主体
    Subject subject = SecurityUtils.getSubject();
    //创建令牌
    UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosi", "12345");

    //登录认证
    try {
        subject.login(usernamePasswordToken);
        System.out.println("认证通过:"+subject.isAuthenticated());
    } catch (UnknownAccountException e) {
        e.printStackTrace();
        System.out.println("用户名错误");
    }catch (IncorrectCredentialsException e){
        e.printStackTrace();
        System.out.println("密码错误!!!");
    }

}

}




---


### 5. shiro中的授权


#### 5.1 授权


授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。


#### 5.2 关键对象


**授权可简单理解为who对what(which)进行How操作:**


`Who,即主体(Subject)`,主体需要访问系统中的资源。


`What,即资源(Resource)`,如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括`资源类型`和`资源实例`,比如`商品信息为资源类型`,类型为t01的商品为`资源实例`,编号为001的商品信息也属于资源实例。


`How,权限/许可(Permission)`,规定了主体对资源的操作许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,通过权限可知主体对哪些资源都有哪些操作许可。


#### 5.3 授权流程


![在这里插入图片描述](https://img-blog.csdnimg.cn/05f82d5f28764cee9e9a1e55503e9b8f.png#pic_center)


#### 5.4 授权方式


* **基于角色的访问控制**


	+ RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制
	
	 
	```
	if(subject.hasRole("admin")){
	   //操作什么资源
	}
	
	```
* **基于资源的访问控制**


	+ RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制
	
	 
	```
	if(subject.isPermission("user:update:01")){ //资源实例
	  //对01用户进行修改
	}
	if(subject.isPermission("user:update:\*")){  //资源类型
	  //对01用户进行修改
	}
	
	```


#### 5.5 权限字符串


​ 权限字符串的规则是:**资源标识符:操作:资源实例标识符**,意思是对哪个资源的哪个实例具有什么操作,“:”是资源/操作/实例的分割符,权限字符串也可以使用\*通配符。


例子:


* 用户创建权限:user:create,或user:create:\*
* 用户修改实例001的权限:user:update:001
* 用户实例001的所有权限:user:\*:001


#### 5.6 shiro中授权编程实现方式


* **编程式**

 

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}

* **注解式**

 

@RequiresRoles(“admin”)
public void hello() {
//有权限
}

* **标签式**

 

JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRole name=“admin”>
<!— 有权限—>
</shiro:hasRole>
注意: Thymeleaf 中使用shiro需要额外集成!

* 


#### 5.7 开发授权


###### 1.realm的实现



public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
System.out.println("primaryPrincipal = " + primaryPrincipal);

    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    simpleAuthorizationInfo.addRole("admin");

    simpleAuthorizationInfo.addStringPermission("user:update:\*");
    simpleAuthorizationInfo.addStringPermission("product:\*:\*");


    return simpleAuthorizationInfo;
}

//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String principal = (String) token.getPrincipal();
    if("xiaochen".equals(principal)){
        String password = "3c88b338102c1a343bcb88cd3878758e";
        String salt = "Q4F%";
        return new SimpleAuthenticationInfo(principal,password, 
                                            ByteSource.Util.bytes(salt),this.getName());
    }
    return null;
}

}


###### 2.授权



public class CustomerMD5RealmTest {

public static void main(String[] args) {

    //创建安全管理器
    DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    //创建自定义MD5Realm对象
    CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
    //创建密码认证匹配器对象
    HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");
    //设置加密的次数
    md5.setHashIterations(1024);
    //设置密码认证匹配器对象
    customerMD5Realm.setCredentialsMatcher(md5);
    //设置安全管理器的 认证安全数据源
    defaultSecurityManager.setRealm(customerMD5Realm);
    //设置安全工具类的安全管理器
    SecurityUtils.setSecurityManager(defaultSecurityManager);

    //获取认证的主体
    Subject subject = SecurityUtils.getSubject();
    //创建令牌
    UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "12345");

    //登录认证
    try {
        subject.login(usernamePasswordToken);
        System.out.println("认证通过:"+subject.isAuthenticated());
    } catch (UnknownAccountException e) {
        e.printStackTrace();
        System.out.println("用户名错误");
    }catch (IncorrectCredentialsException e){
        e.printStackTrace();
        System.out.println("密码错误!!!");
    }

    //基于角色的控制
    //单角色控制
    System.out.println("========hasRole==========");
    boolean admin = subject.hasRole("admin");
    System.out.println("hash admin role:"+admin);

    //多角色控制
    System.out.println("========hasAllRoles==========");
    List<String> roles = Arrays.asList("admin", "user");
    boolean booleans = subject.hasAllRoles(roles);
    System.out.println("booleans = " + booleans);

    // 基于任意角色的控制
    System.out.println("========hasRoles==========");
    boolean[] booleans1 = subject.hasRoles(roles);
    for (boolean b : booleans1) {
        System.out.println("b = " + b);
    }

    //基于权限字符串的权限控制
    System.out.println("========isPermitted==========");

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

s1) {
System.out.println("b = " + b);
}

    //基于权限字符串的权限控制
    System.out.println("========isPermitted==========");

[外链图片转存中…(img-6aRwlpS4-1714280377218)]
[外链图片转存中…(img-Xszy3qrd-1714280377218)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值