Java hibernate 与 Apache Shiro 登录身份验证在“成功”登录后返回 false

Posted

技术标签:

【中文标题】Java hibernate 与 Apache Shiro 登录身份验证在“成功”登录后返回 false【英文标题】:Java hibernate with Apache Shiro login authentication returning false after "successfully" logging in 【发布时间】:2017-05-28 04:00:39 【问题描述】:

我正在使用 Java Hibernate 和 Apache Shiro 进行用户登录和身份验证。我使用此代码对用户进行身份验证,它按预期工作(据我所知)。

 try 
        AuthenticationToken token = new UsernamePasswordToken(user.getUsername().toUpperCase(), user.getPassword());


        Subject currentUser = SecurityUtils.getSubject();

        Session session = currentUser.getSession();
        session.setAttribute( "username", user.getUsername());
        System.out.println("USER: " + session.getAttribute("username"));

        currentUser.login(token);

        System.out.println( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
        System.out.println( "Is user authenticated? " + currentUser.isAuthenticated());
    
    catch (ExcessiveAttemptsException eae )  
         eae.getStackTrace();
         eae.printStackTrace();
     
    catch (AuthenticationException e) 
        throw new ForbiddenException();
    

如果提供了正确的凭据,则此方法有效,如果不正确,则返回禁止的异常。

在我使用另一种方法检查用户是否被授权后,但它总是返回 false(当它应该为 true 时)。我不确定是什么原因造成的。

   try 
        Subject currentUser = SecurityUtils.getSubject();

        return SecurityUtils.getSubject().isAuthenticated();

     catch (AuthenticationException e) 
        throw new ForbiddenException();
    

在调试模式下检查时,currentUser 为 null。

我也可以使用另一种方法,但它会在第二行返回 nullPointerException。

try 

        sessionFactory.getCurrentSession().getTransaction().begin();

        User user = userDAO.find(securityContext.getUserPrincipal().getName());

        if (user == null) 
            sessionFactory.getCurrentSession().getTransaction().rollback();
            throw new NotFoundException();
        

        UserModel result = new UserModel(user);

        sessionFactory.getCurrentSession().getTransaction().commit();

        return result;

     catch (HibernateException e) 
        e.printStackTrace();
        sessionFactory.getCurrentSession().getTransaction().rollback();
    
    return null;

我使用 shiro.ini 连接到本地数据库。 [更新] 我添加了一个调试登录过滤器,它可以在启用时自动登录(使用 Servlet)并按预期工作。 我添加了一个 URL 部分,它在我使用 authcBasic 时有效,但在我在 Shiro 中使用 authc 时无效。我可以通过调用isAuthenticated()getCurrentUser() 方法来检查。使用 authcBasic 返回所有正确信息,但 authc 返回 false/null。除非我清理 Web 浏览器,否则在使用 authcBasic 时我也无法注销。 SecurityUtils.getSubject().logout(); 方法将 isAuthenticated 布尔值设置为 false(当我调试时),但似乎对会话没有任何实际影响。

# =======================
# Shiro INI configuration
# =======================

[main]
# Debug filter to automatically login
debugLogin = util.DebugLoginFilter
debugLogin.enabled = false
debugLogin.username = ADMIN
debugLogin.password = ADMIN

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.authenticationQuery = select password from user where username = ?
jdbcRealm.userRolesQuery = select role from user_role INNER JOIN user ON     user_role.id = user.user_role_id where user.username = ?

# Datasource for the jdbc realm
ds = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
ds.serverName = localhost
ds.user = root
ds.databaseName = database
jdbcRealm.dataSource = $ds

authc.usernameParam = username
authc.passwordParam = password
authc.failureKeyAttribute = shiroLoginFailure

sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
jdbcRealm.credentialsMatcher = $sha256Matcher

[roles]
admin = *

[urls]
/ = debugLogin, authcBasic
/index.html = debugLogin, authcBasic
/api/login = anon
/api/login/me = authcBasic
/api/login/logout = authcBasic
/api/** = debugLogin, authcBasic

我的 pom.xml

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.3.2</version>
</dependency>

我的 web.xml

<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

如果我犯了任何错误,我很抱歉,我是新来的!任何帮助将不胜感激。

编辑:运行 Apache 服务器时,此消息会显示在控制台中:

[localhost-startStop-1] INFO org.apache.shiro.config.IniSecurityManagerFactory - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur.

【问题讨论】:

您在应用程序的哪个位置调用 SecurityUtils.getSubject();那失败了?相同的请求还是另一个请求?您是从过滤器还是衍生线程中调用它? 嗨@teacurran 我从前端调用它(使用Angular 2)作为POST请求将用户名和密码传递给Java后端。 SecurityUtils.getSubject();在用于登录时工作(它在我调试代码时显示所有正确的详细信息),但是当我从前端再次调用它以检查用户是否实际登录/验证时它不起作用。 正确。所以您正在调用一个 GET 请求,该请求正在访问您的 REST api 服务。 REST 服务是如何使用过滤器处理的?小服务程序?如果是过滤器,它可能会在 shiro 过滤器之前被调用,这会导致它不起作用。 @teacurran - 我正在使用扩展 UserFilter 接口的 UserDAO。我在用户登录时使用它来检查它们是否存在(如上面的第一个代码段所示)然后创建一个 UserModel 然后提交更改。我不确定这是否是正确的方法,但似乎错误可能存在,因为用户设置未正确保存。 @teacurran - 我更新了问题并在运行服务器时添加了控制台消息。我在网上找不到任何东西,如果我连接到本地数据库,不确定它是否相关。 【参考方案1】:

我找到了解决办法。问题不在于代码,而在于我使用的方法。

我在前端使用 Angular 2,在后端使用 Java Hibernate。 Shiro 无法与前端通信,因为它位于不同的目录中。但是在构建 Angular 应用程序并将 dist 内容复制到后端的 WebContent 之后,它可以正常工作。

我将在开发时使用调试登录过滤器手动登录(然后在 Shiro 准备好投入生产后将其禁用)。

我将保留代码 sn-ps 以防其他人遇到此问题。

【讨论】:

当我们做currentUser.login()的时候,系统到底是在哪里检查客户端提供的usernamepassword和服务端数据库中存储的呢?

以上是关于Java hibernate 与 Apache Shiro 登录身份验证在“成功”登录后返回 false的主要内容,如果未能解决你的问题,请参考以下文章

Java Web开发之hibernate与mybatis的数据库连接

如何解决 TomEE 8.x、Hibernate 5.4 和 Java 8 的 ASM 问题?

java hibernate错误org/dom4j/DocumentExceptionorg/jboss/logging/BasicLoggerorg/apache/lucene/index...

使用 Apache Shiro 在自定义 Java Web 应用程序与 Drupal 之间进行 SSO 集成

java.lang.ClassNotFoundException: org.hibernate.search.util.logging.impl.LoggerFactory

笔记之_java整理hibernate