英文:
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:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/#!login"/>
<property name="successUrl" value="/"/>
<property name="filterChainDefinitions">
<value>
/favicon.ico = noSessionCreation, anon
/login/ = authc
/logout/ = logout
/** = anon
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<bean id="myRealm" class="com.example.project.ui.view.panels.login.MyAuthRealm">
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
And a web.xml
with some filters:
...
<!-- Apache Shiro -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
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("Unknown Account. Please try again");
} catch (IncorrectCredentialsException ice) {
errorLabel.setValue("Incorrect Credentials. Please try again");
} catch (LockedAccountException lae) {
errorLabel.setValue("This account is locked");
} catch (ExcessiveAttemptsException eae) {
errorLabel.setValue("Too many login attempts. Please try again later");
} catch (AuthenticationException ae) {
errorLabel.setValue("Something went wrong. Please try again later");
}
}
}
});
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<AdminUser> results = this.em.createQuery("SELECT u FROM AdminUser u WHERE u.username ='" + token.getPrincipal() + "' AND u.isActive = TRUE").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 = "admin_user")
@Cacheable(false)
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AdminUser.findAll", query = "SELECT a FROM AdminUser a"),
@NamedQuery(name = "AdminUser.findById", query = "SELECT a FROM AdminUser a WHERE a.id = :id"),
@NamedQuery(name = "AdminUser.findByEmail", query = "SELECT a FROM AdminUser a WHERE a.email = :email"),
@NamedQuery(name = "AdminUser.findByFistName", query = "SELECT a FROM AdminUser a WHERE a.fistName = :fistName"),
@NamedQuery(name = "AdminUser.findByLastName", query = "SELECT a FROM AdminUser a WHERE a.lastName = :lastName"),
@NamedQuery(name = "AdminUser.findByLoginAttempts", query = "SELECT a FROM AdminUser a WHERE a.loginAttempts = :loginAttempts"),
@NamedQuery(name = "AdminUser.findByPassword", query = "SELECT a FROM AdminUser a WHERE a.password = :password"),
@NamedQuery(name = "AdminUser.findByUsername", query = "SELECT a FROM AdminUser a WHERE a.username = :username"),
@NamedQuery(name = "AdminUser.findByRoles", query = "SELECT a FROM AdminUser a WHERE a.roles = :roles")})
public class AdminUser implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
// @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//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 = "email")
private String email;
@Size(max = 100)
@Column(name = "fist_name")
private String fistName;
@Size(max = 100)
@Column(name = "last_name")
private String lastName;
@Basic(optional = false)
@NotNull
@Column(name = "login_attempts")
private int loginAttempts;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 255)
@Column(name = "password")
private String password;
@Size(max = 100)
@Column(name = "username")
private String username;
@Size(max = 255)
@Column(name = "roles")
private String roles;
@Lob
@Size(max = 65535)
@Column(name = "settings")
private String settings;
@Basic(optional = false)
@NotNull
@Column(name = "isActive")
private boolean isActive;
@OneToMany(mappedBy = "adminUserId")
private Collection<Staff> 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<Staff> getStaffCollection() {
return staffCollection;
}
public void setStaffCollection(Collection<Staff> 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'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 && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.model.AdminUser[ id=" + id + " ]";
}
}
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)
专注分享java语言的经验与见解,让所有开发者获益!
评论