28 回复
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;
}
}
@wendal 不反null 返回什么 ?
并且登录成功 也不是返回null
throw new UnknownAccountException();
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 了
登录成功后 doGetAuthorizationInfo 方法也没执行
第一次为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
密码错误,跳出。
这是同一个req 那就检查第一个token是哪里冒出的
通一个 req
仅仅点击了 一次 提交
怎么检查 这个token呢,?
发现 第一个 进来的时候 password 是 页面的值 但是 username是null
然后又进来了一个 这时候 username和password 都是页面的值
去debug这个token类的构造方法,看它是哪里创建的,创建了多少次
@wendal 在没有进入 controller之前 在 FormAuthenticationFilter 里的 excutelogin 产生的 token
不是总共构建了2个token对象吗?我要知道这两个对象是哪里的java代码new出来的
不过,基本上确定是shiro.ini 重复拦截了,需要把第一个token干掉
@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
不知哪里出来的
@javanan shiro.ini里面配置的咯,贴
那另外一个token呢?
这个类的
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()));
登录操作是控制器里面主动login,但shiro filter又做了匹配
所以,现在要解决的就是把第一个token干掉,也就是shiro.ini里面的匹配去掉
@wendal
哦 原来是这样,, 那么直接用 shiro的 登录就好了。。