`

【shiro权限管理】6.shiro认证的代码思路

 
阅读更多

 

后面的部分源码进行了省略,只贴出跟认证主线流程相关代码!

 

shiro是如何做认证的呢?首先回顾一下之前剖析的Shiro的HelloWorld程序中有关认证的部分代码:

//获取当前的Subject
Subject currentUser = SecurityUtils.getSubject();
//测试当前用户是否已经被认证(即是否已经登录)
if (!currentUser.isAuthenticated()) {
    //将用户名与密码封装为UsernamePasswordToken对象
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    token.setRememberMe(true);//记录用户
   try {
        currentUser.login(token);//调用Subject的login方法执行登录
    } catch (UnknownAccountException uae) {
        log.info("There is no user with username of " + token.getPrincipal());
    } catch (IncorrectCredentialsException ice) {
        log.info("Password for account " + token.getPrincipal() + " was incorrect!");
    } catch (LockedAccountException lae) {
       log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                "Please contact your administrator to unlock it.");
    }
}

在上面的代码中,首先获取当前用户的Subject对象,然后通过Subject的isAuthenticated判断用户是否已经登录。如果没有登录,就进行认证,这里开发者需要做的就两点:

1.构建AuthenticationToken实例,一般项目中大都用用户名密码方式校验,则根据前台传的用户名密码构建UsernamePasswordToken

2.调用Subject实例的login方法进行验证

可以想见认证过程都是在这个login方法里进行的,它的内部主要做了两点:

1.获取后台用户信息

2.将前后台获取的用户信息进行比对,验证是否通过

 

 

回顾一下之前的架构:


 

注意其中的Realm,在Shiro的架构中,负责和数据库交互的对象就是Realm对象。这里先点一下,当校验账号与密码时,由Realm提供用户信息,这个由开发者负责实现,而比较密码的工作是Shiro来帮我们完成的

 

 一、获取后台用户信息

那currentUser.login(token);这句代码是如何做验证的,以前面HelloWorld的代码为例:

上面当前用户的Subject的实现类DelegatingSubject的login方法如下:

public void login(AuthenticationToken token) throws AuthenticationException {
   //方法比较长,只要关注这一行就行,可见login是由SecurityManager完成的
    Subject subject = securityManager.login(this, token);
}

 

DefaultSecurityManager的login中:

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info;
        try {
            //调用父类的authenticate方法
            info = authenticate(token);
        } catch (AuthenticationException ae) {
            try {
                onFailedLogin(token, ae, subject);
            } catch (Exception e) {
                if (log.isInfoEnabled()) {
                    log.info("onFailedLogin method threw an " +
                            "exception.  Logging and propagating original AuthenticationException.", e);
                }
            }
            //捕获认证异常做相应处理后再抛出异常,看HelloWorld的代码可以发现在登录相关代码中
           //如果登录失败是根据异常做相关处理的,也就是说只要认证失败就会抛出异常这个很重要,
            throw ae; 
        }
        Subject loggedIn = createSubject(token, info, subject);
        onSuccessfulLogin(token, info, loggedIn);
        return loggedIn;
    }

 

DefaultSecurityManager父类AuthenticatingSecurityManager的authenticate方法:

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    //可以看到认证最后是交给SecurityManager内部维护的认证器authenticator来实现的
    return this.authenticator.authenticate(token);
}

 

本例中的认证器是ModularRealmAuthenticator,执行的是他父类AbstractAuthenticator的authenticate方法:

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
        if (token == null) {
            throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
        }
        log.trace("Authentication attempt received for token [{}]", token);
        AuthenticationInfo info;
        try {
          //实际执行的是doAutenticate方法
            info = doAuthenticate(token);
            if (info == null) {
                String msg = "";
                throw new AuthenticationException(msg);
            }
        } catch (Throwable t) {
            AuthenticationException ae = null;
            if (t instanceof AuthenticationException) {
                ae = (AuthenticationException) t;
            }
            if (ae == null) {
                String msg = "";
                ae = new AuthenticationException(msg, t);
                if (log.isWarnEnabled())
                    log.warn(msg, t);
            }
            try {
                notifyFailure(token, ae);
            } catch (Throwable t2) {
                if (log.isWarnEnabled()) {
                    String msg = "";
                    log.warn(msg, t2);
                }
            }
           //如上所言,如果内部方法doAuthenticate方法报错(也是因为认证不通过)这里将抛出AuthenticationException
           //shiro就是通过这样层层抛出错误的方法来确定认证结果
            throw ae;
        }
        notifySuccess(token, info);
        return info;
    }

 

doAuthenticate执行的是ModularRealmAuthnticator的实现方法:

public AuthenticationInfo doAuthenticate(AuthenticationToken token) throws AuthenticationException {
    assertRealmsConfidured();
    Collection<Realm> realms = getRealms();
    if(realms.size() == 1 ){
        return doSingleRealmAuthentication(realms,iterator().next,authenticationToken);
    }else{
        return doMutiRealmAuthentication(realms,authenticationToken);
    }
}

 

可以看到,在doAuthenticate方法中,首先会获取所有的Realm,然后根据Realm的数量来决定使用单个Realm的校验方法,还是多个Realm的校验方法。我们以配置单个Realm的情况为例doSingleRealmAuthentication方法如下:

protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
       //如上这里认证不通过也是抛出异常 
       if (!realm.supports(token)) {
            String msg = "";
            throw new UnsupportedTokenException(msg);
        }
      //实际调用配置的realm中的getAuthenticationInfo方法
        AuthenticationInfo info = realm.getAuthenticationInfo(token);
        if (info == null) {
            String msg = "";
            throw new UnknownAccountException(msg);
        }
        return info;
    }

 

realm实例类AuthenticatingRealm的getAuthenticationInfo方法如下:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
       //获取缓存的认证信息这里不管
        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
            //实际查找认证信息是这个方法
            info = doGetAuthenticationInfo(token);
        } else {
        }
        if (info != null) {
           //这里验证密码后面会提到
            assertCredentialsMatch(token, info);
        } else {
        }
        return info;
    }

 

在HelloWorld例子中用的ini配置文件,所以自动配置了简单的SimpleAccountRealm,它的doGetAuthenticationInfo方法:

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
       //这里的SimpleAccount实现了AuthenticationInfo接口
        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;
    }

 到这里我们就跟开发者需要做的另外一件事对接上了,那就是实现Realm接口,包括它的doGetAuthenticationInfo方法,我们实现这个方法要做的就是如果找到认证信息则返回,如果找不到或者账号被锁住则抛出相应异常如上面提到的

 

二、密码验证

到这里login方法内部有了前台传过来的用户名密码,以及从Realm实现类中获取的用户信息,还缺一步就是两个信息的比对,也就是密码验证。这一步在哪实现呢?

往上找AuthenticatingRealm这个类,也就是一般我们实现Realm都会继承的抽象类,它里面的assertCredentialsMatch方法:

//此方法没有返回值比对通过方法结束,否则抛出异常
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        CredentialsMatcher cm = getCredentialsMatcher();
        if (cm != null) {
           //doCredentialsMatch就是比对密码的地方
            if (!cm.doCredentialsMatch(token, info)) {
                String msg = "";
                throw new IncorrectCredentialsException(msg);
            }
        } else {
            throw new AuthenticationException("");
        }
    }

 CredentialsMatcher的实现类这里是SimpleCredentialsMatcher它的doCredentialsMatch方法实现如下:

//这个方法很简单就是将token中的密码跟后台获取用户信息中的密码进行比对,返回比对结果
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = getCredentials(token);
        Object accountCredentials = getCredentials(info);
        return equals(tokenCredentials, accountCredentials);
    }

 到此shiro认证过程结束,当然这个是在前面HelloWorld例子中的流程,不过一般web项目的实现内部也大差不差

 

 

 

  • 大小: 47.4 KB
  • 大小: 30.5 KB
分享到:
评论

相关推荐

    springboot+springdatajpa+thymeleaf+shiro 的管理平台框架

    Apache Shiro是一个强大的安全框架,主要用于身份认证、授权、会话管理和加密。在本管理平台中,Shiro起到了安全管理的作用。它负责用户登录验证、权限控制,确保只有合法用户才能访问特定资源。Shiro的简单API和...

    iherus-shiro-redis-master.zip_iherus_redis

    Apache Shiro是一个强大且易用的Java安全框架,提供身份认证、授权、会话管理和加密等功能。其设计目标是让安全相关的代码更易于理解、开发和维护。在本系统中,Shiro负责处理用户的登录验证、角色权限分配以及访问...

    focus,用shiro和springboot搭建的权限管理系统,用户、角色、资源管理授权,细粒度到权限码.zip

    Shiro是一个强大的Java安全框架,它简化了身份验证(登录)、授权(权限控制)、会话管理和密码加密等安全相关任务。SpringBoot则是一个流行的微服务开发框架,它简化了Spring的配置,使得快速开发变得更加容易。将...

    SSM+Maven+Mysql+shiro无需重启动态权限

    5. **Apache Shiro**:Shiro是一个强大的安全框架,提供了认证、授权、会话管理和加密等功能。在动态权限方面,Shiro可以动态地控制用户的访问权限,如角色、权限等,而无需重启服务。这意味着在运行时,可以根据...

    毕设-库存管理系统.zip

    Apache Shiro是一个强大的安全管理框架,用于处理认证、授权、会话管理和加密等安全问题。在库存管理系统中,Shiro负责用户的身份验证和权限控制,确保只有合法用户才能访问相应的资源,增强了系统的安全性。 5. *...

    权限控制系统

    总的来说,这个项目提供了一个实际的权限控制系统的实例,对于想要学习SSM框架和Shiro权限管理的开发者来说,是一个很好的实践平台。通过对源代码的研究,不仅可以掌握基本的框架使用,还能了解权限控制的设计思路和...

    毕业设计java作业管理系统设计(源代码+论文).zip

    这意味着学生或者开发者可以通过查看源代码来理解系统的实现细节,而论文则能帮助他们了解设计思路和背后的理论支持。 【系统设计】 在Java作业管理系统的设计中,通常会涉及到以下几个关键知识点: 1. **MVC设计...

    基于SpringBoot的权限管理系统

    Shiro提供了用户认证、授权、会话管理和安全性相关的API,轻量级且易于集成,适合小型到中型项目。它能轻松实现登录验证、角色权限控制等功能。 3. **MyBatis**:持久层框架MyBatis负责数据访问,通过XML或注解的...

    277考试管理系统.zip

    5. **安全机制**:系统可能使用Spring Security或Apache Shiro进行用户认证和授权,保护考试的公正性。 6. **前端技术**:可能使用HTML、CSS和JavaScript,配合jQuery、Vue.js或React.js等前端框架,构建交互式的...

    java作业管理系统设计(源代码+论文).rar

    4. **用户认证与授权**:为了确保系统安全,作业管理系统通常需要实现用户登录功能,涉及到身份验证和权限控制。这可能涉及Spring Security或Apache Shiro等框架。 5. **前端技术**:系统界面可能使用HTML、CSS和...

    javaweb-就业信息管理系统.zip

    7. **安全控制**:系统可能包含用户认证和授权功能,如使用Spring Security或Apache Shiro来实现权限管理,防止未授权访问。 8. **前端技术**:为了构建用户友好的界面,开发者可能会使用HTML、CSS和JavaScript,...

    关于Apache shiro实现一个账户同一时刻只有一个人登录(shiro 单点登录)

    Apache Shiro 是一个强大且易用的 Java 安全框架,它提供了认证、授权、加密和会话管理功能,可以非常方便地与任何应用服务层集成。在 Shiro 中实现单点登录(Single Sign-On,SSO)是为了确保同一账号在同一时间...

    javaiHRM人力资源管理系统项目实战视频教程

     6、系统认证授权的解决方案|  7、云存储解决方案|  8、RBAC权限设计方案|  9、刷脸登录解决方案|  10、自定义代码生成器|  11、Activiti工作流开发| 涵盖知识点:  1.结合Activiti7工作流引擎的应用教程。 ...

    javaEE土地档案管理系统.zip

    可能采用Spring Security或Apache Shiro等框架来实现权限管理。 10. **性能优化**:为了提高系统性能,可能需要进行数据库查询优化、缓存策略设计、负载均衡配置等。 通过这个项目,开发者不仅可以学习到JavaEE...

    javaEE健康管理系统.rar

    7. **安全机制**:系统应包含用户认证和授权机制,例如使用Spring Security或Apache Shiro,确保只有合法用户可以访问敏感信息。此外,还需关注SQL注入、XSS攻击等安全问题,通过过滤输入和使用预编译SQL防止这些...

    Guns后台管理系统 v7.3.4.zip

    此外,系统还集成了Shiro安全框架,提供用户认证和授权功能,确保系统安全运行。 在功能方面,Guns系统提供了权限管理、角色管理、用户管理、菜单管理等基础功能,满足了企业级后台管理需求。其中,权限管理模块...

    Maven spring+springMvc+MyBatics+Redis+Shiro+PageHelp+Quartz+Log4j

    5. Shiro:Apache Shiro是一个安全框架,提供认证、授权、会话管理和加密等功能。它简单易用,可以方便地集成到现有应用中,保护资源的安全访问。 6. PageHelp:PageHelp通常是一个分页帮助类,用于处理数据查询时...

    基于springboot的教务管理系统源码.zip

    这部分通常涉及到Spring Security或者Apache Shiro进行安全控制,实现用户认证与授权。 2. 课程管理:课程的增删改查,包括课程信息、教师安排、学生选课等。这需要数据库设计合理,可能用到MyBatis或JPA作为持久层...

    毕业设计 JAVA物业管理系统设计与实现(论文+源代码).zip

    9. **安全控制**:如Spring Security或Apache Shiro,用于用户认证和授权。 10. **单元测试**:JUnit或TestNG进行代码测试,确保系统功能的正确性。 11. **版本控制**:如Git的使用,跟踪代码变更和团队协作。 12. *...

    基于springMVC的商户管理系统源码.zip

    3. **安全机制**:如Spring Security或Apache Shiro,用于实现用户认证和授权,保护系统资源。 4. **前后端交互**:可能采用JSON格式通过Ajax进行数据交换,使用jQuery或其他JavaScript库增强用户体验。 5. **模板...

Global site tag (gtag.js) - Google Analytics