【问题标题】:Shiro with Springboot integrationShiro 与 Spring Boot 集成
【发布时间】:2016-12-20 06:42:14
【问题描述】:

我已将 Apache Shiro 与 Spring Boot 与 Spring Data JPA 集成。 Spring Boot 项目位于this GitHub repo。

问题是当我运行并尝试验证应用程序时出现以下错误

roleAdmin.getId() 1: null
roleAdmin.getId() 2: 3
Current user is not authenticated.
2016-08-13 09:49:45.715  WARN 10528 --- [lication Thread]  o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: S0022
2016-08-13 09:49:45.716 ERROR 10528 --- [lication Thread] o.h.engine.jdbc.spi.SqlExceptionHelper   : Column 'id' not found.
Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - yunus, rememberMe=false].   Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).

它完全无法通过身份验证,我设法创建了this repo 来详细说明我的问题。看看吧。

解决方案和批评是高度可接受的。

更新

如果需要任何额外信息来澄清我的问题,请询问

【问题讨论】:

    标签: spring hibernate spring-boot spring-data-jpa


    【解决方案1】:

    您的错误消息表明存在问题,它位于您的用户存储库的@Query 定义中:

    @Query(value = "SELECT u.username FROM users u WHERE u.username = ?1", nativeQuery = true)
    User findByUsername(String username);
    

    如您所见,您只选择了用户名,而不是选择每一列。由于您使用的是 Spring Data JPA,因此您根本不需要 @Query,只需说:

    User findByUsername(String username);
    

    但是,您的另一个问题是如何比较自定义领域中的密码。来自数据库的密码将被加密,这意味着你不能只说

    user.getPassword().equals(new String(upat.getPassword()))
    

    您必须使用DefaultPasswordService 调用其passwordsMatch 方法来比较密码,并且由于您自己验证了密码,因此您应该在您的领域中使用AllowAllCredentialsMatcher

    public class CustomSecurityRealm extends AuthorizingRealm {
    
        @Autowired
        private UserManagerService userManager;
    
        @Autowired
        private DefaultPasswordService passwordService;
    
        public CustomSecurityRealm() {
            this(new AllowAllCredentialsMatcher());
        }
    
        public CustomSecurityRealm(final CredentialsMatcher matcher) {
            super(matcher);
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            // remains the same
        }
    
         @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken upat = (UsernamePasswordToken) token;
            User user = userManager.findByUsername(upat.getUsername());                             
            if(user != null && passwordService.passwordsMatch(upat.getPassword(), user.getPassword())) {
                return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
            }
            else {
                throw new AuthenticationException("Invalid username/password combination!");
            }
        }
    }
    

    【讨论】:

    • 现在可以使用了,感谢您。这个问题一直困扰着我。
    • 太棒了!顺便说一句,您发布的可运行示例帮助很大,大约需要 5 分钟才能找到问题,如果只有更多人会这样做:)