org.apache.shiro.authc.UnknownAccountException: Realm [com.coracle.fast.service.impl.shiro.ShiroRealm@3ef2de86] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - null, rememberMe=false (127.0.0.1)].
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:184)
at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:270)
at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:256)
at org.apache.shiro.web.filter.authc.AuthenticatingFilter.executeLogin(AuthenticatingFilter.java:53)
at org.apache.shiro.web.filter.authc.FormAuthenticationFilter.onAccessDenied(FormAuthenticationFilter.java:154)
at org.apache.shiro.web.filter.AccessControlFilter.onAccessDenied(AccessControlFilter.java:133)
at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:162)
at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:203)
at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:178)
at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:131)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2517)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2506)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:744)
贴realm代码,全部
来自炫酷的 NutzCN
package com.coracle.fast.service.impl.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.coracle.fast.entity.shiro.Permission;
import com.coracle.fast.entity.shiro.Role;
import com.coracle.fast.entity.shiro.User;
import com.coracle.fast.service.UserService;
@Service("shiroRealm")
@Transactional(readOnly = true)
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
if (principalCollection == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
// 获取登录时输入的用户名
String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
// 到数据库查是否有此对象
User user = userService.findByUserName(loginName);
if (null == user) {
return null;
}
if (user.isLocked()) {
throw new LockedAccountException("Account [" + user.getUserName() + "] is locked.");
}
// 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
if (user.getRoles() != null) {
for (Role role : user.getRoles()) {
auth.addRole(role.getName());
if (role.getPermissions() != null) {
for (Permission p : role.getPermissions()) {
auth.addStringPermission(p.getName());
}
}
}
}
if (user.getPermissions() != null) {
for (Permission p : user.getPermissions()) {
auth.addStringPermission(p.getName());
}
}
return auth;
}
/**
* 登录认证;
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
// UsernamePasswordToken对象用来存放提交的登录信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 查出是否有此用户
User user = userService.findByUserName(token.getUsername());
if (user != null) {
// 若存在,将此用户存放到登录认证info中
// 用户名 密码 slat realm name
return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(),
ByteSource.Util.bytes(user.getSalt()), getName());
}
return null;
}
}
不能返回null
来自炫酷的 NutzCN
@wendal 不反null 返回什么 ?
并且登录成功 也不是返回null
debug 了一个 发现执行顺序是这样的
/**
* 认证回调函数,登录时调用.
* 登录认证;
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
// UsernamePasswordToken对象用来存放提交的登录信息
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 查出是否有此用户
User user = userService.findByUserName(token.getUsername());
if (user != null) {
// 若存在,将此用户存放到登录认证info中
// 用户名 密码 slat realm name
return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(),
ByteSource.Util.bytes(user.getSalt()), getName());
}else{
throw new UnknownAccountException();
}
}
1、执行 User user = userService.findByUserName(token.getUsername());
发现 user 是 null
2、 throw new UnknownAccountException();
然后没跳出方法,反而 有执行了 一边 User user = userService.findByUserName(token.getUsername());
这个时候 发现 user 不是null
但是有由于密码错误
抛出 IncorrectCredentialsException
这样就导致了 不管登录成功还是失败 都出现了 上面的 bug
是的 ,
第一次 User user = userService.findByUserName(token.getUsername()); user 是null (数据库有数据)
就执行了
throw new UnknownAccountException();
这时候 程序又执行了 一遍 User user = userService.findByUserName(token.getUsername()); user 这时候不是null 了
第一次为null, 这必须先查清楚
User user = userService.findByUserName(token.getUsername()); // user 是null (数据库有数据)
打印 token.getUsername()
的值出来
log.debug("name=["+token.getUsername()+"]");
发现是 点击 submit后
没有到controller,现在执行了
User user = userService.findByUserName(token.getUsername()); // user 是null (数据库有数据)
这时候 getUsername 是 null
抛出异常,
执行了 controller
执行 User user = userService.findByUserName(token.getUsername());
这个时候 getUsername 是 页面的 admin
密码错误,跳出。
@wendal 在没有进入 controller之前 在 FormAuthenticationFilter 里的 excutelogin 产生的 token
@wendal 另一个是我 页面请求 form表单 产生的
@wendal ??
@wendal
第一个 token
public abstract class AuthenticatingFilter extends AuthenticationFilter {
public static final String PERMISSIVE = "permissive";
//TODO - complete JavaDoc
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
AuthenticationToken token = createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
"must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
}
try {
Subject subject = getSubject(request, response);
subject.login(token);
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
return onLoginFailure(token, e, request, response);
}
}
这里 AuthenticationToken token = createToken(request, response);
产出来的
这个 过滤 AuthenticationFilter
不知哪里出来的
这个类的
public class ModularRealmAuthenticator extends AbstractAuthenticator
这里
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "]. Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}
@wendal
在自己的
controller
里 传过去的
SecurityUtils.getSubject().login(new UsernamePasswordToken(user.getUserName(), user.getPassword()));