也不知道是什么原因,或许是经常在网上看到它的好处,不过写个博客确实可以整理下自己学到的知识,方便以后查找。废话不多,越来越有写篇博客的想法下面讲讲我是怎样在项目中加入shiro,然后再项目中怎样使用shiro实现权限管理,以及在使用shiro的过程中遇到的为题以及解决的方法。
1、怎样在项目中加入shiro
因为项目是使用osgi+spring+jpa实现,使用maven的方式通过pom文件添加ja包,所以只需要在pom.xml文件中添加下面这些依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Description>${project.description}</Bundle-Description>
<Embed-Dependency>shiro-core,shiro-ehcache,shiro-web,shiro-quartz,shiro-spring,httpclient</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
<Embed-Directory>dependency</Embed-Directory>
<Import-Package>
Ok,现在已经把shiro相关的jar包加到项目中,下面将在spring的beans.xml文件中配置shiro相关的bean和属性,项目使用Ehcache管理shiro的缓存(为了方便,下面已经把shiro跟spring相关的bean配置全部粘贴下来),期中shiro的realm自己实现,新建一个类继承Realm类即可。
<?xml version="1.0" encoding="GBK"?>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for additional
information regarding copyright ownership. The ASF licenses this file to
You under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License. -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
xmlns:camel="http://camel.apache.org/schema/spring" xmlns:bean="http://servicemix.apache.org/bean/1.0"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<!-- 缓存管理器 使用Ehcache实现 -->
<bean id="cacheManager"
class="cn.gzydt.infoshare.security.cache.impl.DefaultCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache.xml" />
</bean>
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator"
class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
<!-- 会话DAO -->
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache" />
<property name="sessionIdGenerator" ref="sessionIdGenerator" />
</bean>
<!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler"
class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000" />
<property name="sessionManager" ref="sessionManager" />
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager"
class="cn.gzydt.infoshare.security.session.impl.SessionManagerImpl">
<!-- session -->
<property name="globalSessionTimeout" value="${security_shiro.globalSessionTimeout}" />
<property name="deleteInvalidSessions" value="true" />
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
<property name="sessionDAO" ref="sessionDAO" />
</bean>
<!-- Realm实现 -->
<bean id="userRealm" class="cn.gzydt.infoshare.security.realm.custom.UserRealm">
<property name="entityServer" ref="entityServer"></property>
<property name="systemService" ref="systemService"></property>
<property name="cachingEnabled" value="true" />
<property name="authenticationCachingEnabled" value="false" />
<property name="authenticationCacheName" value="authenticationCache" />
<property name="authorizationCachingEnabled" value="true" />
<property name="authorizationCacheName" value="authorizationCache" />
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<property name="realms">
<list>
<ref bean="userRealm" />
</list>
</property>
<property name="sessionManager" ref="sessionManager" />
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
<!-- Shiro生命周期处理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>
至此,已经把shiro交给了spring进行管理,下面也把Ehcache缓存的配置也粘出来(在项目路径下新建一个文件夹Ehcache,然后再新建一个Ehcache.xml文件,把下面的配置复制到Ehcache.xml文件中即可,不懂的地方百度一下,网上很多)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">
<diskStore path="java.io.tmpdir"/>
<cache name="passwordRetryCache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="authorizationCache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="authenticationCache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro-activeSessionCache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="SSOCache"
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
import org.apache.shiro.util.Destroyable;
import org.apache.shiro.util.Initializable;
/**
* 缓存管理
* @author
*
*/
public interface CacheManager extends org.apache.shiro.cache.CacheManager, Initializable, Destroyable{
}
import cn.gzydt.infoshare.security.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
/**
*
* @author
*
*/
public class DefaultCacheManager extends EhCacheManager implements CacheManager{
}
2、至此,shiro已经完全加到项目中,并且可以使用了,下面说说怎样在项目中使用shiro实现权限管理
新一个类或接口MyUserRealm继承AuthorizingRealm,Realm,然后覆写 doGetAuthorizationInfo,doGetAuthenticationInfo方法
<span style="font-size:18px;">public interface SecurityRealm extends Realm {
}</span>
public class UserRealm extends AuthorizingRealm implements SecurityRealm {
private EntityServer entityServer;
private SystemService systemService;
@Override
@Transactional
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals){
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User) entityServer.findByOneProperty(Entity.USER,
"userName", username).get(0);
if (User.isStop(user)) {
throw new AuthorizationException("用户已经停用,请联系管理员!");
}
Set<String> permissions = new HashSet<String>();
// 获取用户所拥有角色的权限
for (Role r : user.getRoles()) {
if (r.getKey().equals("Administrator")) {
// 如果是管理员
authorizationInfo.addStringPermission("*");
return authorizationInfo;
}
if (!Role.isStop(r)) {
//如果当前角色是启用状态
for (Permission p : r.getPermissions()) {
if (!Systemz.isStop(systemService.findSystemByPermission(p))) {
//如果系统是启用状态
permissions.add(p.getKey());
}
}
}
}
// 获取用户所在组的权限
for (Group g : user.getGroups()) {
if (!Group.isStop(g)) {
//如果当前组是启用状态
for (Permission p : g.getPermissions()) {
if (!Systemz.isStop(systemService.findSystemByPermission(p))) {
//如果系统是启用状态
permissions.add(p.getKey());
}
}
}
}
// 获取用户的权限
if (!User.isStop(user)) {
//如果当前用户是启用状态
for (Permission p : user.getPermissions()) {
if (!Systemz.isStop(systemService.findSystemByPermission(p))) {
//如果系统是启用状态
permissions.add(p.getKey());
}
}
}
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
/**
* 登录,帐号密码验证
*/
@Override
@Transactional
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
//System.err.println("**************************************** 登录 *****************************************");
User user = (User) entityServer.findByOneProperty(Entity.USER,
"userName", username).get(0);
if (user == null||!user.getUserName().equals(username)) {
throw new UnknownAccountException("帐号或密码错误");// 没找到帐号
}
if (!password.equals(user.getPassword())) {
throw new IncorrectCredentialsException("帐号或密码错误");
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUserName(), // 用户名
user.getPassword(), // 密码
getName() // realm name
);
return authenticationInfo;
}
public void setEntityServer(EntityServer entityServer) {
this.entityServer = entityServer;
}
public void setSystemService(SystemService systemService) {
this.systemService = systemService;
}
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
}
整个代码说得简单些就是验证用户名和密码的时候,先通过username从数据库中查找出用户交给realm,然后让shiro自己判断用户是否合法,认证权限的时候,先把用户的所有权限通过字符串的方式交给shiro,然后认证的时候判断shiro的缓存中是否存在需要认证的字符串,有就通过,没有就报异常,然后通过catch异常的类型返回不同的信息。
try {
subject = loginService.login(username, password, systemKey);
sessionId = subject.getSession().getId().toString();
message = Message.SUCCESS;
} catch (AuthenticationException e) {
e.printStackTrace();
HttpSession session = request.getSession();
session.setMaxInactiveInterval(-1);
session.setAttribute(Const.SESSION_ID, sessionId);
message = Message.USERNAME_OR_PASSWORD_ERROR;
resultJson.put(Const.MESSAGE, message);
logger.info(username, Message.USER_LOGIN, Message.LOGIN_RESULT_FAILED);
return buildResult(resultJson);
} catch (AuthorizationException e) {
e.printStackTrace();
HttpSession session = request.getSession();
session.setMaxInactiveInterval(-1);
session.setAttribute(Const.SESSION_ID, sessionId);
message = Message.AUTH_FOR_LOGIN_ERROR;
resultJson.put(Const.MESSAGE, message);
logger.info(username, Message.USER_LOGIN, Message.LOGIN_RESULT_FAILED);
return buildResult(resultJson);
} catch (Exception e) {
e.printStackTrace();
logger.info(username, Message.USER_LOGIN, Message.LOGIN_RESULT_FAILED);
}
OK,到这里我们已经通过shiro实现了自己的权限策略,下一篇将会说说在使用shiro的过程中遇到的问题。