密码管理员更改用户密码后出现了“IncorrectCredentialsException”异常。

huangapple 未分类评论47阅读模式
英文:

IncorrectCredentialsException after administrator changes user password

问题

I understand your code and the issue you're facing. Here's the translated version of your provided content:


我刚刚开始接触Apache Shiro,因为我“继承”了一些代码,其中存在以下错误,请容我描述一下。

当管理员更改用户的密码时,该用户无法登录。
输入新更改的密码会返回IncorrectCredentialsException(不正确的凭据异常)。检查数据库,密码已经更改。

奇怪的是,如果输入旧密码,可以登录。登出后,可以使用新密码。

  • 该应用程序是基于Vaadin 8框架构建的Java应用程序。
  • 持久性用于连接到MySQL数据库

没有shiro.ini文件,我能找到的只有一个security.xml文件:

<!-- 在这里是security.xml文件的内容 -->

以及一个web.xml文件中的一些过滤器:

<!-- 在这里是web.xml文件的内容 -->

从我所理解的情况来看,使用了Shiro的默认实现,没有扩展任何自定义功能,例如会话或缓存。

这是登录按钮的监听器的代码:

// 在这里是登录按钮监听器的代码

这是自定义Realm MyAuthRealm 中的doGetAuthenticationInfo方法:

// 在这里是doGetAuthenticationInfo方法的代码

正如我在上面的注释中所提到的,问题位于results变量,由于某种原因,查询返回了管理员在更改密码之前的密码,这就是为什么会收到IncorrectCredentialsException异常。

这是AdminUser模型类的代码:

// 在这里是AdminUser模型类的代码

您是否对可能发生的情况有任何想法,以便我能为您指引正确的方向?


英文:

I'm just starting with Apache Shiro, since I "inherited" some code which has the following bug, so bear with me please.

When the administrator changes the password of a user, that user cannot login.
Entering the newly changed password returns an IncorrectCredentialsException. Checking the database, the password has been changed.

The weird thing is that if you enter the old password, you can login. After loging out, you can use your new password.

  • The application is Java, built on the Vaadin 8 framework.
  • Persistence is used to connect to the MySQL database

There is no shiro.ini file, all I can find is a security.xml file:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd&quot;&gt;

    &lt;bean id=&quot;shiroFilter&quot; class=&quot;org.apache.shiro.spring.web.ShiroFilterFactoryBean&quot;&gt;
        &lt;property name=&quot;securityManager&quot; ref=&quot;securityManager&quot;/&gt;
        &lt;property name=&quot;loginUrl&quot; value=&quot;/#!login&quot;/&gt;
        &lt;property name=&quot;successUrl&quot; value=&quot;/&quot;/&gt;
        &lt;property name=&quot;filterChainDefinitions&quot;&gt;
            &lt;value&gt;
                /favicon.ico = noSessionCreation, anon
                /login/ = authc
                /logout/ = logout
                /** = anon
            &lt;/value&gt;
        &lt;/property&gt;
    &lt;/bean&gt;

    &lt;bean id=&quot;securityManager&quot; class=&quot;org.apache.shiro.web.mgt.DefaultWebSecurityManager&quot;&gt;
        &lt;property name=&quot;realm&quot; ref=&quot;myRealm&quot;/&gt;
    &lt;/bean&gt;

    &lt;bean id=&quot;myRealm&quot; class=&quot;com.example.project.ui.view.panels.login.MyAuthRealm&quot;&gt;
    &lt;/bean&gt;

    &lt;bean id=&quot;lifecycleBeanPostProcessor&quot; class=&quot;org.apache.shiro.spring.LifecycleBeanPostProcessor&quot;/&gt;

    &lt;!-- Enable Shiro Annotations for Spring-configured beans.  Only run after --&gt;
    &lt;!-- the lifecycleBeanProcessor has run: --&gt;
    &lt;bean class=&quot;org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator&quot;
          depends-on=&quot;lifecycleBeanPostProcessor&quot;/&gt;
    &lt;bean class=&quot;org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor&quot;&gt;
        &lt;property name=&quot;securityManager&quot; ref=&quot;securityManager&quot;/&gt;
    &lt;/bean&gt;

&lt;/beans&gt;

And a web.xml with some filters:

...
&lt;!-- Apache Shiro --&gt;
    &lt;filter&gt;
        &lt;filter-name&gt;shiroFilter&lt;/filter-name&gt;
        &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;targetFilterLifecycle&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
        &lt;/init-param&gt;
    &lt;/filter&gt;
    &lt;filter-mapping&gt;
        &lt;filter-name&gt;shiroFilter&lt;/filter-name&gt;
        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    &lt;/filter-mapping&gt;
...

From what I can understand, the default implementation of Shiro has been used and nothing custom has been extended, for example for sessions or cache.

This is the login button listener

loginButton.addClickListener(new Button.ClickListener() {
    public void buttonClick(Button.ClickEvent event) {
        boolean isFormValid = true;
        if (isFormValid) {
            UsernamePasswordToken token = new UsernamePasswordToken((String) usernameField.getValue(), (String) passField.getValue());
            Subject currentUser = SecurityUtils.getSubject();
            try {
                currentUser.login(token);
                Page.getCurrent().reload();
                
            } catch (UnknownAccountException uae) {
                errorLabel.setValue(&quot;Unknown Account. Please try again&quot;);
            } catch (IncorrectCredentialsException ice) {
                errorLabel.setValue(&quot;Incorrect Credentials. Please try again&quot;);
            } catch (LockedAccountException lae) {
                errorLabel.setValue(&quot;This account is locked&quot;);
            } catch (ExcessiveAttemptsException eae) {
                errorLabel.setValue(&quot;Too many login attempts. Please try again later&quot;);
            } catch (AuthenticationException ae) {
                errorLabel.setValue(&quot;Something went wrong. Please try again later&quot;);
            }
        }
    }
});

And this is the the doGetAuthenticationInfo from the custom Realm, MyAuthRealm

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
        throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    //  Problem is located here.
    //  For some reason this query returns the old password
    List&lt;AdminUser&gt; results = this.em.createQuery(&quot;SELECT u FROM AdminUser u WHERE u.username =&#39;&quot; + token.getPrincipal() + &quot;&#39; AND u.isActive = TRUE&quot;).getResultList();
    if (results.size() == 1) {
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(((AdminUser) results.get(0)).getUsername(), ((AdminUser) results.get(0)).getPassword(), getName());
        return info;
    }
    return null;
}

As I mention in the comments above the results variable, for some reason, the query return the password before the administrator changed it and that's why I get the IncorrectCredentialsException.

This is the AdminUser model Class:

@Entity
@Table(name = &quot;admin_user&quot;)
@Cacheable(false)
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = &quot;AdminUser.findAll&quot;, query = &quot;SELECT a FROM AdminUser a&quot;),
    @NamedQuery(name = &quot;AdminUser.findById&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.id = :id&quot;),
    @NamedQuery(name = &quot;AdminUser.findByEmail&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.email = :email&quot;),
    @NamedQuery(name = &quot;AdminUser.findByFistName&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.fistName = :fistName&quot;),
    @NamedQuery(name = &quot;AdminUser.findByLastName&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.lastName = :lastName&quot;),
    @NamedQuery(name = &quot;AdminUser.findByLoginAttempts&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.loginAttempts = :loginAttempts&quot;),
    @NamedQuery(name = &quot;AdminUser.findByPassword&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.password = :password&quot;),
    @NamedQuery(name = &quot;AdminUser.findByUsername&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.username = :username&quot;),
    @NamedQuery(name = &quot;AdminUser.findByRoles&quot;, query = &quot;SELECT a FROM AdminUser a WHERE a.roles = :roles&quot;)})
public class AdminUser implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = &quot;id&quot;)
    private Integer id;
    // @Pattern(regexp=&quot;[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&amp;&#39;*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?&quot;, message=&quot;Invalid email&quot;)//if the field contains email address consider using this annotation to enforce field validation
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = &quot;email&quot;)
    private String email;
    @Size(max = 100)
    @Column(name = &quot;fist_name&quot;)
    private String fistName;
    @Size(max = 100)
    @Column(name = &quot;last_name&quot;)
    private String lastName;
    @Basic(optional = false)
    @NotNull
    @Column(name = &quot;login_attempts&quot;)
    private int loginAttempts;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = &quot;password&quot;)
    private String password;
    @Size(max = 100)
    @Column(name = &quot;username&quot;)
    private String username;
    @Size(max = 255)
    @Column(name = &quot;roles&quot;)
    private String roles;
    @Lob
    @Size(max = 65535)
    @Column(name = &quot;settings&quot;)
    private String settings;
    @Basic(optional = false)
    @NotNull
    @Column(name = &quot;isActive&quot;)
    private boolean isActive;
    @OneToMany(mappedBy = &quot;adminUserId&quot;)
    private Collection&lt;Staff&gt; staffCollection;

    public AdminUser() {
    }

    public AdminUser(Integer id) {
        this.id = id;
    }

    public AdminUser(Integer id, String email, int loginAttempts, String password) {
        this.id = id;
        this.email = email;
        this.loginAttempts = loginAttempts;
        this.password = password;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getFistName() {
        return fistName;
    }

    public void setFistName(String fistName) {
        this.fistName = fistName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getLoginAttempts() {
        return loginAttempts;
    }

    public void setLoginAttempts(int loginAttempts) {
        this.loginAttempts = loginAttempts;
    }

    public String getPassword() {
        return password;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }

    public String getSettings() {
        return settings;
    }

    public void setSettings(String settings) {
        this.settings = settings;
    }

    public boolean isIsActive() {
        return isActive;
    }

    public void setIsActive(boolean isActive) {
        this.isActive = isActive;
    }

    @XmlTransient
    public Collection&lt;Staff&gt; getStaffCollection() {
        return staffCollection;
    }

    public void setStaffCollection(Collection&lt;Staff&gt; staffCollection) {
        this.staffCollection = staffCollection;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won&#39;t work in the case the id fields are not set
        if (!(object instanceof AdminUser)) {
            return false;
        }
        AdminUser other = (AdminUser) object;
        if ((this.id == null &amp;&amp; other.id != null) || (this.id != null &amp;&amp; !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return &quot;com.model.AdminUser[ id=&quot; + id + &quot; ]&quot;;
    }

}

Do you have any idea about what might be going one, so you could steer me in the right direction?

答案1

得分: 0

可能涉及到缓存,并且在更新用户密码时需要使特定领域的缓存失效(或者禁用缓存)。

英文:

There is likely a cache involved, and you need to invalidate the cache of a given realm when you update the user’s password (or disable caching)

huangapple
  • 本文由 发表于 2020年7月23日 22:24:02
  • 转载请务必保留本文链接:https://java.coder-hub.com/63056547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定