Shiro的简单介绍以及使用

Shiro的使用

1. 什么是权限管理

权限管理属于安全管理的范畴,简单的说就是对用户的访问进行控制,你是什么身份,你就能访问到什么级别的数据

2. 什么是身份认证

判断一个用户,是否是合法用户的处理的这个过程叫做身份认证,最常用的就是通过简单的用户名和密码,进行身份验证,
如果你的用户名和密码在数据库中是存在的,那么说明你的身份就是合法的,否则,不合法

3.关键对象 (名词解释)

//Subject: 主体: 访问该系统的用户,程序,在咱们的上面进行认证的都称为主体,简单的说,谁去认证那么这个主体就是谁:
//Principal:身份信息:就是Subject进行身份认证的标识,标识必须具有唯一性 比如说:手机号,用户名,邮箱,简单的说,就是你带什么信息去进行认证,那么这个Principal就是什么
//credential:凭证信息,密码,安全信息

4. 基本的权限模型

什么是权限模型?
模型简单来说,就相当于一个套路,这个套路简单的说就是我们在做项目的时候,如果遇到一个项目中,有很多种不同身份的用户(角色)使用这个系统,而且不同身份的这些用户还拥有不同的访问权限,我们在设置数据库的时候的这个套路

eg: 商城项目:角色----平台  卖家  买家
	不同身份的用户还具有不同的访问权限
	角色: 实际上就是权限的一个集合

资源:官方的解释就是 一切能够被计算机识别的图片文字和文件等等都被称为资源
项目开发种的资源: 指的是页面上的所有的按钮,图片 文字,超链接等等

5. 通用的权限模型

六张表 
用户表---(用户角色表)---角色表---(角色权限表)---权限表-------资源表
用户角色表是多对对关系    角色权限表 多对多         权限 一对一 资源表

六张表可以简化成五张表
用户表---(用户角色表)---角色表---(角色权限表)---权限资源表

6.表中的字段

//用户表
用户id  用户名  ....
 
//角色表
角色id  角色名 ....
 
 //用户角色表
 用户id  角色id (这里设置复合主键)
  
// 权限资源表
权限资源id 权限名 权限描述 type(per|res) resName resPath 显示区域的编码
 
//角色权限表
角色id  权限id

7. 目前市场上通用的权限管理的框架

shiro  Spring Security    OAuth2
// Spring Security  这个框架是有依赖性的 Spring
// OAuth2:第三方登录,公众平台
//shiro :这个框架有个优点,没有框架的依赖,任何平台都可以用

8.shiro简介

shiro就是一个授权和认证的这样一个框架
简单的说,就是以前咱们认证和授权的所有代码,shiro都给咱们写好了,而且封装好了,我们只需要按照这个框架提供的Api来简单的集成到我们的项目种就可以了

9.shiro的功能

认证  授权  Cache管理  Session管理  rememberMe功能的实现  登录 退出

10.shiro中常见的名词解释

Subject:登录的这个用户,谁认证谁就是这个主体
Principal:用户名
credential:密码
Token:令牌(用户名+密码的封装)---进行认证的封装对象
SecurityManager:安全管理器(只要使用了shiro框架那么这个对象都是必不可少的)
Authenticator:认证器:主要做用户身份认证,就是登录的时候用来做身份校验的
Authrizer:授权器:用来给用户授权
Realm:用户认证和授权的时候,和数据库进行交互的对象(这里面做的事情就是从数据库查询数据 封装成token然后进行认证和授权)

11. shiro的第一个程序

11.1 导包

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.4.0</version>
        </dependency>

11.2 编写配置文件,模拟数据库

[users]
chaochao=123
xiaowang=456

11.3 代码

public class ShiroTest {
    public static void main ( String[] args ) {
//        获取安全管理的工厂
        IniSecurityManagerFactory iniSecurityManagerFactory =
                new IniSecurityManagerFactory("classpath:shiro.ini");
//        获取安全管理器
        SecurityManager securityManager = iniSecurityManagerFactory.createInstance();
//        将安全管理器设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
//        获取当前操作的主体
        Subject subject = SecurityUtils.getSubject();
//        将用户名和密码丢进去进行测试,用户名和密码都是前端传过来的
//        创建token
        UsernamePasswordToken token = new
                UsernamePasswordToken("chaochao", "123");
//        传递token进行检验
        subject.login(token);
//        查看返回结果
        boolean b = subject.isAuthenticated();
        System.out.println(b);

    }
}

12. shiro源码的解读

public class DelegatingSubject implements Subject {
   public void login(AuthenticationToken token) throws AuthenticationException {
        this.clearRunAsIdentitiesInternal();
     //完成认证
        Subject subject = this.securityManager.login(this, token);
public class DefaultSecurityManager extends SessionsSecurityManager {
  							// token 是前端传过来的值(usernameAndpasswordtoken)
  public Subject login(Subject subject, AuthenticationToken token) throws    		    											AuthenticationException {
        AuthenticationInfo info;
        try {
          // 进行认证的地方
            info = this.authenticate(token);
        } catch (AuthenticationException var7) {
          // 如果有异常意味着认证失败
            AuthenticationException ae = var7;

            try {
              // 调用认证失败的方法
                this.onFailedLogin(token, ae, subject);
            } catch (Exception var6) {
                if(log.isInfoEnabled()) {
                    log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
                }
            }

            throw var7;
        }
			//用户认证成功,就封装成subject
        Subject loggedIn = this.createSubject(token, info, subject);
    //  调用认证成功的方法
        this.onSuccessfulLogin(token, info, loggedIn);
        return loggedIn;
    }
public abstract class AuthenticatingSecurityManager extends RealmSecurityManager
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
  // 调用了校验器中的authenticate方法
        return this.authenticator.authenticate(token);
    }
public abstract class AbstractAuthenticator implements Authenticator, LogoutAware {
  
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
        if(token == null) {
            throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
        } else {
            log.trace("Authentication attempt received for token [{}]", token);

            AuthenticationInfo info;
            try {
              // 实现认证的方法
                info = this.doAuthenticate(token);

public class ModularRealmAuthenticator extends AbstractAuthenticator {

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        this.assertRealmsConfigured();
        Collection<Realm> realms = this.getRealms();
        return realms.size() == 1?
          //  进行认证
          this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken):this.doMultiRealmAuthentication(realms, authenticationToken);
    }
 protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
        if(!realm.supports(token)) {
            String msg = "Realm [" + realm + "] does not support authentication token [" + token + "].  Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type.";
            throw new UnsupportedTokenException(msg);
        } else {
          // 认证
            AuthenticationInfo info = realm.getAuthenticationInfo(token);
            if(info == null) {
                String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "].";
                throw new UnknownAccountException(msg);
            } else {
                return info;
            }
        }
    }
 public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
        if(info == null) {
          // 认证的地方
            info = this.doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if(token != null && info != null) {
                this.cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if(info != null) {
          // 校验密码
            this.assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken)token;
   // 通过用户名获取用户
        SimpleAccount account = this.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 boolean equals(Object tokenCredentials, Object accountCredentials) {
        if(log.isDebugEnabled()) {
            log.debug("Performing credentials equality check for tokenCredentials of type [" + tokenCredentials.getClass().getName() + " and accountCredentials of type [" + accountCredentials.getClass().getName() + "]");
        }

        if(this.isByteSource(tokenCredentials) && this.isByteSource(accountCredentials)) {
            if(log.isDebugEnabled()) {
                log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing array equals comparison");
            }
		//比较密码的byte类型是否一致,如果一致,就返回true
            byte[] tokenBytes = this.toBytes(tokenCredentials);
            byte[] accountBytes = this.toBytes(accountCredentials);
            return MessageDigest.isEqual(tokenBytes, accountBytes);
        } else {
            return accountCredentials.equals(tokenCredentials);
        }
    }

13. shiro与realm

13.1 自定义一个realm

public class MyShiro extends AuthorizingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo ( AuthenticationToken authenticationToken ) throws AuthenticationException {
//        通过token获取到用户数据
        Object userName = authenticationToken.getPrincipal();
//        在数据库中通过用户名进行查询,是否存在这个用户
 //       ....
//        模拟查出的用户数据
        User user = new User(1, "chaochao", "1234");
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getName(), user.getPassword(), getName());
        return authenticationInfo;
    }

13.2 编写配置文件

[main]
customRealm=com.wcc.springboot.shiro.MyShiro
securityManager.realms=$customRealm

13.3 编写测试类

public class ShiroTest {
    public static void main ( String[] args ) {
//        获取安全管理的工厂
        IniSecurityManagerFactory iniSecurityManagerFactory =
                new IniSecurityManagerFactory("classpath:shiro-realm.ini");
//        获取安全管理器
        SecurityManager securityManager = iniSecurityManagerFactory.createInstance();
//        将安全管理器设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
//        获取当前操作的主体
        Subject subject = SecurityUtils.getSubject();
//        将用户名和密码丢进去进行测试,用户名和密码都是前端传过来的
//        创建token
        UsernamePasswordToken token = new
                UsernamePasswordToken("chaochao", "1234");
//        传递token进行检验
        subject.login(token);
//        查看返回结果
        boolean b = subject.isAuthenticated();
        System.out.println(b);

    }
}

14. shiro认证中常见的两个异常

14.1 用户名不存在异常

Exception in thread "main" org.apache.shiro.authc.UnknownAccountException:

14.2 密码错误异常

Exception in thread "main" org.apache.shiro.authc.IncorrectCredentialsException

15. MD5Hash的使用

在数据库存储的时候,一些敏感信息不能以明文的方式存储,shiro中也提供了密码散列

15.1 Md5简单使用

public class Md5Test {
    public static void main ( String[] args ) {
        Md5Hash md5Hash = new Md5Hash("ABC");
        System.out.println("散列后的值是"+md5Hash);
        System.out.println("----------------------------------");
//        加盐,为什么加盐,给这个密码再添加一层保障
//        为了让密码更加安全,更加不容易被破解
        Md5Hash md5Hash1 = new Md5Hash("abc", "123");
        System.out.println("加盐散列后的值"+md5Hash1);
        Md5Hash md5Hash2 = new Md5Hash("123abc");
        System.out.println("加盐散列后的值"+md5Hash2);
//        md5Hash1=md5Hash2 两个值是相同的,相当于将盐放在密码前进行散列
        System.out.println(md5Hash1.equals(md5Hash2));
        System.out.println("-----------------------------------");
        Md5Hash md5Hash3 = new Md5Hash("abc", "123", 2);
        System.out.println("加盐+两次散列后的值"+md5Hash3);
        Md5Hash md5Hash4 = new Md5Hash(md5Hash1);
        System.out.println("加盐+散列后的值再次散列"+md5Hash4);
//      md5Hash3=md5Hash4
        System.out.println(md5Hash3.equals(md5Hash4));

        System.out.println("-----------------------------------");
      
      /*
      上面代码的运行结果
      
      散列后的值是902fbdd2b1df0c4f70b4a5d23525e932
      ----------------------------------
      加盐散列后的值a906449d5769fa7361d7ecc6aa3f6d28
      加盐散列后的值a906449d5769fa7361d7ecc6aa3f6d28
      true
      -----------------------------------
      加盐+两次散列后的值4c25782028c1edf8f4e122fd71d7c37c
      加盐+散列后的值再次散列4c25782028c1edf8f4e122fd71d7c37c
      true
      -----------------------------------
* */

15.2 realm和Md5Hash的使用

15.2.1 自定义一个realm
public class Md5Realm extends AuthorizingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo
            ( AuthenticationToken authenticationToken )
            throws AuthenticationException {
        Object userName = authenticationToken.getPrincipal();
        if (!userName.equals("chaochao")){
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo("chaochao",
//                        存储在数据库中的密码
                        "a906449d5769fa7361d7ecc6aa3f6d28",
//                        从数据库取的盐值
                        ByteSource.Util.bytes("123"),
                        "Md5Realm");
        return simpleAuthenticationInfo;
    }
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo ( PrincipalCollection principalCollection ) {
        return null;
    }
15.2.2 编写配置文件
[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1

#将凭证匹配器设置到realm
customRealm=com.wcc.springboot.shiro.Md5Realm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
15.2.3 测试
public class Md5RealmTest {
    public static void main ( String[] args ) {
//        获取安全管理器的工厂
        IniSecurityManagerFactory factory =
                new IniSecurityManagerFactory(
                        "classpath:shiro-hash.ini");
//        获取安全管理器
        SecurityManager securityManager = factory.getInstance();
//        将安全管理器设置进运行环境
        SecurityUtils.setSecurityManager(securityManager);
//        获取主体
        Subject subject = SecurityUtils.getSubject();
//        获取token
        UsernamePasswordToken token = new
                UsernamePasswordToken("chaochao", "abc");
        subject.login(token);
//        判断是否认证成功
        boolean b = subject.isAuthenticated();
        System.out.println(b);
    }
}

16. 代码授权

用户要授权,必须是在认证的基础上进行授权

16.1 自定义realm

public class Md5Realm extends AuthorizingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo
            ( AuthenticationToken authenticationToken )
            throws AuthenticationException {
        Object userName = authenticationToken.getPrincipal();
        if (!userName.equals("chaochao")){
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo("chaochao",
//                        存储在数据库中的密码
                        "a906449d5769fa7361d7ecc6aa3f6d28",
//                        从数据库取的盐值
                        ByteSource.Util.bytes("123"),
                        "Md5Realm");
        return simpleAuthenticationInfo;
    }
  // 授权的代码
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo (
            PrincipalCollection principalCollection ) {
//        1. 通过前端传过来的数据获取用户名
        Object userName = principalCollection.getPrimaryPrincipal();
//        2. 通过用户名在数据库中查询出角色,和相应的权限
//        。。。。。
//        假设这是从数据库查出来的权限
        Set<String> perms = new HashSet<>();
        perms.add("user:add");
        perms.add("user:delete");
//        假设这是从数据库中查出来的角色
        Set<String> roles = new HashSet<>();
        roles.add("buyer");
        roles.add("soler");
        SimpleAuthorizationInfo authorizationInfo =
                new SimpleAuthorizationInfo(roles);

        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }
    
}

16.2测试

public class Md5RealmTest {
    public static void main ( String[] args ) {
//        获取安全管理器的工厂
        IniSecurityManagerFactory factory =
                new IniSecurityManagerFactory(
                        "classpath:shiro-hash.ini");
//        获取安全管理器
        SecurityManager securityManager = factory.getInstance();
//        将安全管理器设置进运行环境
        SecurityUtils.setSecurityManager(securityManager);
//        获取主体
        Subject subject = SecurityUtils.getSubject();
//        获取token
        UsernamePasswordToken token = new
                UsernamePasswordToken("chaochao", "abc");
        subject.login(token);
//        判断是否认证成功
        boolean b = subject.isAuthenticated();
        System.out.println(b);

//        测试 ,当用户具有这个权限时打印。。。
        if (subject.isPermitted("user:add")){
            System.out.println("yes ,you are clear");
        }

        //表示的是一个一个判断 用户是否具有 某一个权限 (返回的是数组)
        boolean[] permitted = subject.isPermitted("", "", "");
        //表示的是一个一个判断 用户是否具有 某一个权限 (返回的是数组)
        ArrayList<Permission> list = new ArrayList<>();
        boolean[] booleans = subject.isPermitted(list);

        //玩下角色 (一个一个判断是否具有某一个角色)
        boolean[] booleans1 = subject.hasRoles(new ArrayList<String>());
        boolean b1 = subject.hasRole("");//判断是否具有 某一个角色
        boolean b2 = subject.hasAllRoles(new ArrayList<String>());//判断是否具有所有权限
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值