关于spring整合shiro授权验证不通过的问题

jackyCoding 发布于 2013/12/09 12:12
阅读 24K+
收藏 1

applicationContext-shiro.xml配置文件信息如下

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd"
default-lazy-init="true">


<description>Shiro Configuration</description>


    <!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath*:database.properties" />

<!-- 安全认证过滤器 -->
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 没有权限或者失败后跳转的页面 -->
<property name="loginUrl" value="${adminPath}/login" />
<property name="successUrl" value="${adminPath}" />
<property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
        <!-- 主要内容是shiroFilter的定义。
loginUrl:登陆页面,用户登陆不成功,自动返回此页面。
successUrl:登陆成功后跳转此页面
unauthorizedUrl:用户访问无权限的链接时跳转此页面
filterChainDefinitions:设置url的访问权限。anon表示不用验证,都可以访问。anthc:authc filter 监听,
不登陆不能访问。logout:logout filter监听。没有列出的常用配置:perms["remote:invoke"] :
需要角色romote 和权限invoke才能访问。roles["admin"]需要角色admin才能访问。设置可用“,”隔开,如:
/admin/test = authc,roles[admin]
安全认证过滤器 -->
<property name="filterChainDefinitions">
<value>
/static/** = anon
${adminPath}/login = authc
${adminPath}/logout = logout
${adminPath}/** = user
</value>
</property>
</bean>

<!-- 定义 Shiro 主要业务对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- <property name="sessionManager" ref="sessionManager" /> -->
<property name="realm" ref="systemAuthorizingRealm" />
<!-- <property name="cacheManager" ref="shiroCacheManager" />  -->
</bean>
<!-- 
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">  
   <property name="globalSessionTimeout" value="3600000" /> 
   <property name="sessionDAO" ref="sessionDAO"/>
</bean>


<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean> -->

      <!-- 用户授权信息Cache, 采用EhCache -->
<!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="cacheManager"/>
</bean> -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />  
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- AOP式方法级权限检查  -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>

<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

</beans>

SystemAuthorizingRealm 方法如下 

public class SystemAuthorizingRealm extends AuthorizingRealm {
    @Autowired
private SystemService systemService;
/**
* 认证回调函数, 登录时调用
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
try {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
if (LoginController.isValidateCodeLogin(token.getUsername(), false, false)){
// 判断验证码
Session session = SecurityUtils.getSubject().getSession();
String code = (String)session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)){
throw new CaptchaException("验证码错误.");
}
}
User user = systemService.getUserByLoginName(token.getUsername());
if (user != null) {
byte[] salt = Encodes.decodeHex(user.getPassword().substring(0,16));

return new SimpleAuthenticationInfo(new Principal(user), 
user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
} else {
return null;
}
} catch (InvalidSessionException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return null;
}


/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Principal principal = (Principal) getAvailablePrincipal(principals);
User user =systemService.getUserByLoginName(principal.getLoginName());
if (user != null) {
UserUtils.putCache("user", user);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<Privilege> list = UserUtils.getPrivilegeList();
for (Privilege menu : list){
if (StringUtils.isNotBlank(menu.getPermission())){
// 添加基于Permission的权限信息
info.addStringPermission(menu.getPermission());
}
}
// 更新登录IP和时间
//systemService.updateUserLoginInfo(user.getId());
return info;
} else {
return null;
}
}

/**
* 设定密码校验的Hash算法与迭代次数
*/
public void initCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SystemService.HASH_ALGORITHM);
matcher.setHashIterations(SystemService.HASH_INTERATIONS);
setCredentialsMatcher(matcher);
}

/**
* 清空用户关联权限认证,待下次使用时重新加载
*/
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
clearCachedAuthorizationInfo(principals);
}


/**
* 清空所有关联认证
*/
public void clearAllCachedAuthorizationInfo() {
Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
if (cache != null) {
for (Object key : cache.keys()) {
cache.remove(key);
}
}
}


/**
* 获取系统业务对象
*/
/* public SystemService getSystemService() {
if (systemService == null){
systemService = SpringContextHolder.getBean(SystemService.class);
}
System.out.println("++++++++++++++++++"+systemService);
return systemService;
}*/

/**
* 授权用户信息
*/
public static class Principal implements Serializable {


private static final long serialVersionUID = 1L;

private Long id;
private String loginName;
//private String name;
private Map<String, Object> cacheMap;


public Principal(User user) {
this.id = user.getId();
this.loginName = user.getLoginName();
//this.name = user.getName();
}


public Long getId() {
return id;
}


public String getLoginName() {
return loginName;
}


/* public String getName() {
return name;
}*/


public Map<String, Object> getCacheMap() {
if (cacheMap==null){
cacheMap = new HashMap<String, Object>();
}
return cacheMap;
}


}
}



用户加密如下

user.setPassword(SystemService.entryptPassword(password));

public static final String HASH_ALGORITHM = "SHA-1";
public static final int HASH_INTERATIONS = 1024;
public static final int SALT_SIZE = 8;


/**
* 生成安全的密码,生成随机的16位salt并经过1024次 sha-1 hash
*/
public static String entryptPassword(String plainPassword) {
byte[] salt = Digests.generateSalt(SALT_SIZE);
byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), salt, HASH_INTERATIONS);
return Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword);
}

/**
* 验证密码
* @param plainPassword 明文密码
* @param password 密文密码
* @return 验证成功返回true
*/
public static boolean validatePassword(String plainPassword, String password) {
byte[] salt = Encodes.decodeHex(password.substring(0,16));
byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), salt, HASH_INTERATIONS);
return password.equals(Encodes.encodeHex(salt)+Encodes.encodeHex(hashPassword));
}

代码看上去没有错误,但是就是到SystemAuthorizingRealm类的return new SimpleAuthenticationInfo(new Principal(user), 
user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());
报错,错误信息如下org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [com.lejie.protal.sys.security.UsernamePasswordToken - admin, rememberMe=false (0:0:0:0:0:0:0:1)] did not match the expected credentials.


有哪位大神能指点下,在下不胜感激,谢谢


加载中
0
安静听歌
安静听歌
题外话,小弟有个不解的地方,你的doGetAuthenticationInfo方法里边怎么没有密码验证的代码的?就像:密码1.equals(密码2) 这样的代码,请问在哪里呢?我在云码的ThinkGem/JeeSite项目中也看到这样的代码,可是我顺着shiro源码看下去,最终都没找到equals的代码,最后是委托doGetAuthenticationInfo方法的呢,谢谢
安静听歌
安静听歌
额,找到验证密码部分的代码了,原来这样写的doGetAuthenticationInfo只充当了Dao的功能,将用户信息拿出来,真正校验就交给assertCredentialsMatch方法(XX.AuthenticatingRealm.assertCredentialsMatch) 所以,在自己的realm中重写该方法就可以自定义密码之间的equals 额,,,感谢Debug功能,哈哈~
返回顶部
顶部