Apache shiro笔记 (1)入门

本文介绍了Shiro权限系统的原理和应用,包括基于角色和资源的访问控制(RBAC)概念,以及如何使用Shiro进行身份认证和授权。通过示例展示了如何在Java项目中配置Shiro,使用ini文件和数据库作为Realm进行用户认证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:

https://jinnianshilongnian.iteye.com/blog/2018936

http://how2j.cn/k/shiro/shiro-tutorial/1720.html#nowhere

RBAC 是当下权限系统的设计基础,同时有两种解释:
1.: Role-Based Access Control,基于角色的访问控制
即,你要能够删除产品,那么当前用户就必须拥有产品经理这个角色
2.:Resource-Based Access Control,基于资源的访问控制
即,你要能够删除产品,那么当前用户就必须拥有删除产品这样的权限

开源权限管理项目:

Spring Security   

Apache Shiro     

 

Apache Shiro是Java的一个安全框架,基本功能点如图:

Authentication身份认证/登录,验证用户是不是拥有相应的身份;

Authorization授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

Cryptography加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web SupportWeb支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrencyshiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing提供测试支持;

Run As允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

以用户登录为例子,

 

应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:

Subject主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

最简单的一个Shiro应用:

1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

一.接下来入门一个通过ini的简单登录的例子:

1.使用idea创建一个普通的java项目  

新手可以参照:https://blog.youkuaiyun.com/oschina_41790905/article/details/79475187

2.引入两个jar包 

   新手关于jar包引入可以参考https://blog.youkuaiyun.com/qq_28289405/article/details/82216847

3.在src目录下新建 shiro.ini
 用户数据  此处充当realm域
 

#定义用户
[users]
#用户名 zhang3  密码是 12345
zhang3 = 12345
#用户名 li4  密码是 abcde
li4 = abcde

3. 在src目录下创建 包结构,创建User.class

public class User {


    private String name;
    private String password;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

 

4. 创建 TestShiro

准备3个用户,前两个能在 shiro.ini 中找到,第3个不存在
然后测试登录

public class TestShiro {
    public static void main(String[] args) {
        User zhang3 = new User();
        zhang3.setName("zhang3");
        zhang3.setPassword("12345");

        User li4 = new User();
        li4.setName("li4");
        li4.setPassword("abcde");

        User wang5 = new User();
        wang5.setName("wang5");
        wang5.setPassword("wrongpassword");
        List<User> users = new ArrayList<>();
        users.add(zhang3);
        users.add(li4);
        users.add(wang5);

      
        //登陆每个用户
        for (User user : users) {

            if(login(user))
                System.out.printf("%s \t成功登陆,用的密码是 %s\t %n",user.getName(),user.getPassword());
            else
               System.out.printf("%s \t成功失败,用的密码是 %s\t %n",user.getName(),user.getPassword());
        }

    }

    private static boolean login(User user) {
        Subject subject = getSubject(user);
        //如果已经登录过了,退出
        if (subject.isAuthenticated())
            subject.logout();
        //封装用户的数据
//token可以理解为用户令牌,登录的过程被抽象为Shiro验证令牌是否具有合法身份以及相关权限。
        UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
        try {
            //将用户的数据token 最终传递到Realm中进行对比
            subject.login(token);
        } catch (AuthenticationException e) {
            //验证错误
            return false;
        }
        return subject.isAuthenticated();

    }

    private static Subject getSubject(User user) {
        //加载配置文件,并获取工厂
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //获取安全管理者实例
        SecurityManager sm = factory.getInstance();

        //将安全管理者放入全局对象,注入SecurityManager
        SecurityUtils.setSecurityManager(sm);

        //全局对象通过安全管理者生成Subject对象
        Subject subject = SecurityUtils.getSubject();

        return subject;
    }


}

5.  运行查看结果

二、通过数据库充当realm域

1.创建表

前面提到过,RBAC 的概念, RBAC会存在3 张基础表: 用户,角色,权限, 以及 2 张中间表来建立 用户与角色的多对多关系,角色与权限的多对多关系。 用户与权限之间也是多对多关系,但是是通过 角色间接建立的。

 

2.  添加jar包

简单介绍一下几个jar包:

commons-beanutils是Apache开源组织提供的用于操作JAVA BEAN的工具包。使用commons-beanutils,我们可以很方便的对bean对象的属性进行操作。参考:https://blog.youkuaiyun.com/jianggujin/article/details/51104949

slf4j:Simple Logging Facade for Java,为java提供的简单日志Facade。Facade门面,更底层一点说就是接口。它允许用户以自己的喜好,在工程中通过slf4j接入不同的日志系统。 参考:https://www.cnblogs.com/lujiango/p/8573411.html

因此slf4j入口就是众多接口的集合,它不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体有哪些接口,全部都定义在slf4j-api中。它只提供一个核心slf4j api(就是slf4j-api.jar包),这个包只有日志的接口,并没有实现,所以如果要使用就得再给它提供一个实现了些接口的日志包,比 如:log4j,common logging,jdk log日志实现包等。

Jakarta  Commons-logging(JCL)是apache最早提供的日志的门面接口。提供简单的日志实现以及日志解耦功能。                ---------------------------------------------------------------------------https://www.cnblogs.com/xiadongqing/p/5208824.htm

mysql-connector-java-5.0.8-bin JDBC驱动

3. 

public class DatabaseRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //能进入到这里,表示账号已经通过验证了
        String userName =(String) principalCollection.getPrimaryPrincipal();
        //通过DAO获取角色和权限
        Set<String> permissions = new DAO().listPermissions(userName);
        Set<String> roles = new DAO().listRoles(userName);

        //授权对象
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        //把通过DAO获取到的角色和权限放进去
        s.setStringPermissions(permissions);
        s.setRoles(roles);
        return s;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取账号密码
        UsernamePasswordToken t = (UsernamePasswordToken) token;
        String userName= token.getPrincipal().toString();
        String password= new String( t.getPassword());
        //获取数据库中的密码
        String passwordInDB = new DAO().getPassword(userName);

        //如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不是抛出具体错误原因,免得给破解者提供帮助信息
        if(null==passwordInDB || !passwordInDB.equals(password))
            throw new AuthenticationException();

        //认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
        SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName());
        return a;
    }
}

4.修改ini文件

[main]
databaseRealm=com.jxw.DatabaseRealm
securityManager.realms=$databaseRealm

5. 运行结果与之前相同

Maven项目入门

 

身份验证,即在应用中谁能证明他就是他本人。一般提供如他们的身份ID一些标识信息来表明他就是他本人,如提供身份证,用户名/密码来证明。

在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份:

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。

credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

最常见的principals和credentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。

另外两个相关的概念是之前提到的SubjectRealm,分别是主体及验证主体的数据源。

基本的身份认证

1. 通过idea创建一个maven项目

项目结构如图

 

 

2. 在pom.xml中添加如下的代码,引入相关需要的jar包

<dependencies>  
    <dependency>  
        <groupId>junit</groupId>  
        <artifactId>junit</artifactId>  
        <version>4.9</version>  
    </dependency>  
    <dependency>  
        <groupId>commons-logging</groupId>  
        <artifactId>commons-logging</artifactId>  
        <version>1.1.3</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-core</artifactId>  
        <version>1.2.2</version>  
    </dependency>  
</dependencies>

3.创建shiro.ini  用户身份/凭据

通过[users]指定了两个主体:zhang/123、wang/123。

即zhang为用户名 123位密码

[users]
zhang=123
wang=123

4.编写测试用例 LoginTest.java

public class LoginTest {
    @Test
    public  void test1(){
        //    1、首先通过new IniSecurityManagerFactory并指定一个ini配置文件来创建一个SecurityManager工厂;
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//    2、接着获取SecurityManager并绑定到SecurityUtils,这是一个全局设置,设置一次即可;
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
//    3、通过SecurityUtils得到Subject,其会自动绑定到当前线程;如果在web环境在请求结束时需要解除绑定;然后获取身份验证的Token,如用户名/密码;
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("zhang", "1");
//    4、调用subject.login方法进行登录,其会自动委托给SecurityManager.login方法进行登录;
//    5、如果身份验证失败请捕获AuthenticationException或其子类,常见的如: DisabledAccountException(禁用的帐号)、
// LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、
// ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException (错误的凭证)、
// ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;
// 对于页面的错误消息展示,最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,
// 防止一些恶意用户非法扫描帐号库;
        try{subject.login(token);}catch (AuthenticationException e){
            System.out.print("登录失败");
        }
        Assert.assertEquals(true,subject.isAuthenticated());
            System.out.print("登录成功");
        //    6、最后可以调用subject.logout退出,其会自动委托给SecurityManager.logout方法退出
        subject.logout();
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值