浅谈Shiro配置

Apache Shiro是一个强大的,易用的Java安全框架。它被用作于认证,授权,加密,session管理。依赖于Shiro简单易懂的API,就可以快速的构建包括手机,大型web和商业应用。

下面介绍简单的shiro配置:
首先在工程Maven依赖里面引入shiro:
           

<dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>

其次在Web.xml里面配置shiro拦截器:

<dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>

 
 然后在web.xml添加shiro配置文件路径读取:

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/spring/appCtx-dubbo.xml
            classpath:/spring/appCtx-aop.xml
            classpath:/spring/appCtx-base.xml
            classpath:/spring/appCtx-cache.xml
            classpath:/spring/appCtx-shiro.xml
        </param-value>
    </context-param>

 
 

然后就可以编辑自己Reaml类进行权限控制,示例如下:


 

package com.onlyou.olyfinance.common.shiro;

import java.util.*;

import com.onlyou.olyfinance.common.constant.CodeInfoConstants;
import com.onlyou.olyfinance.common.util.BeanUtils;
import com.onlyou.olyfinance.common.util.CarrierUtil;
import com.onlyou.olyfinance.sys.entity.CarrierEntity;
import com.onlyou.olyfinance.sys.service.IUsrAuthService;
import com.onlyou.olyfinance.sys.service.IUsrRoleRelService;
import com.onlyou.olyfinance.sys.vo.MenuVO;
import com.onlyou.olyfinance.sys.vo.TreeMenuVO;
import com.onlyou.olyfinance.sys.vo.UsrManageVO;
import com.onlyou.olyfinance.sys.vo.UsrRoleRelManageVO;
import com.onlyou.olyfinance.usr.vo.LoginUserVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.onlyou.framework.exception.BusinessException;

/**
 * shiro权限控制
 * 
 */
public class MngRealm extends AuthorizingRealm {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private IUsrAuthService usrAuthService;
	@Autowired
	private IUsrRoleRelService usrRoleRelService;

	/**
	 * 认证 获取认证信息
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authToken;
		logger.debug("用户登录:" + token.getUsername());
		try {
			UsrManageVO user = usrAuthService.findUserByName(token
					.getUsername(),CarrierUtil.getCarrierId());
			if (null == user) {
				throw new BusinessException("用户名或密码不正确");
			}
			LoginUserVO loginUser = new LoginUserVO();
			loginUser.setId(user.getId());
			loginUser.setCarrierId(user.getCarrierId());
			loginUser.setUsrAccount(user.getUsrAccount());
			loginUser.setHeadPicUri(user.getAvatarFileId());
			loginUser.setTicket(UUID.randomUUID().toString());
			loginUser.setAdminFlag(CodeInfoConstants.YES_STATUS_VALUE
					.equals(user.getIsSysAdmin()));
			loginUser.setUserName(user.getStaffNm());
			AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(
					loginUser, user.getUsrPwd(), getName());
				return authcInfo;
		} catch (BusinessException e) {
			logger.error(e.getMessage(), e);
			throw new AuthenticationException(e.getMessage(), e);
		} catch (Throwable t) {
			logger.error(t.getMessage(), t);
			throw new AuthenticationException("未知错误,请查看日志", t);
		}
	}

	/**
	 * 校验密码处理
	 */
	@Override
	protected void assertCredentialsMatch(AuthenticationToken token,
	                                      AuthenticationInfo info) throws AuthenticationException {
		try {
			super.assertCredentialsMatch(token, info);
		} catch (ExcessiveAttemptsException e) {
			throw e;
		} catch (IncorrectCredentialsException e) {
			logger.error(e.getMessage(), e);
			throw new AuthenticationException("用户名或密码不正确", e);
		} catch (Throwable t) {
			logger.error(t.getMessage(), t);
			throw new AuthenticationException("未知错误,请查看日志", t);
		}
		// 校验通过进行初始化角色,菜单,授权等信息
		this.initLoginUserVO((LoginUserVO) info.getPrincipals()
				.getPrimaryPrincipal());
	}

	/**
	 * 授权 鉴权回调函数,提取当事人的角色和权限 角色、菜单/按钮权限
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		SimpleAuthorizationInfo authInfo = new SimpleAuthorizationInfo();
		LoginUserVO loginUser = (LoginUserVO) principals.getPrimaryPrincipal();
		if (loginUser != null) {
			// 权限初始化
			Map<String, UsrRoleRelManageVO> roleMap = loginUser
					.getRoleMap();
			if (roleMap != null) {
				for (UsrRoleRelManageVO role : roleMap.values()) {
					authInfo.addRole(role.getRoleEncrypt());
				}
				logger.info("roles: " + authInfo.getRoles());
			}
			// permissions
			List<MenuVO> menuList = loginUser.getMenuList();
			if (menuList != null) {
				for (MenuVO menu : menuList) {
					String menuUri = menu.getMenuUri();
					if (StringUtils.isNotBlank(menuUri)) {
						authInfo.addStringPermission(menuUri);
					}
				}
				logger.info("menu permissions: "
						+ authInfo.getStringPermissions());
			}
		}
		return authInfo;
	}

	/**
	 * 清空权限(运营商角色判断是否重置)
	 *
	 * @param principals
	 * @param initFlag
	 */
	public void cleanCache(PrincipalCollection principals, boolean initFlag) {
		LoginUserVO loginUser = (LoginUserVO) principals.getPrimaryPrincipal();
		if (loginUser != null) {
			if (initFlag) {
				this.initLoginUserVO(loginUser);
			}
			super.clearCachedAuthorizationInfo(principals);
		}
	}

	/**
	 * 加载角色,菜单,权限的信息
	 *
	 * @param loginUser
	 */
	private void initLoginUserVO(LoginUserVO loginUser) {
		UsrRoleRelManageVO roleCondition = new UsrRoleRelManageVO();
		roleCondition.setUsrId(loginUser.getId());
		roleCondition.setIsValid(CodeInfoConstants.YES_STATUS_VALUE);
		List<UsrRoleRelManageVO> roleList = usrRoleRelService
				.list(roleCondition);
		Map<String, UsrRoleRelManageVO> roleMapAll = new HashMap<>();
		for (UsrRoleRelManageVO roleVO : roleList) {
			String roleId = roleVO.getRoleId();
			if(CodeInfoConstants.YES_STATUS_VALUE.equals(roleVO.getIsDefaultRole())){
				loginUser.setRoleId(roleId);
			}
			roleMapAll.put(roleId, roleVO);
		}
		loginUser.setRoleMap(roleMapAll);
		// 缓存上菜单列表供页面使用
		Set<MenuVO> menuSet =new HashSet<>();
		for (UsrRoleRelManageVO roleVO : roleMapAll.values()) {
			List<MenuVO> menuList = usrAuthService
					.getMenusByRolePermission(roleVO.getRoleId());
			menuSet.addAll(menuList);
		}
		List<MenuVO> menuList =new ArrayList<>(menuSet);
		//因为HashSet是无续的,这里重新排序
		Collections.sort(menuList, new Comparator<MenuVO>() {
			@Override
			public int compare(MenuVO o1, MenuVO o2) {
				return o1.getOrderNo().compareTo(o2.getOrderNo());
			}
		});
		loginUser.setMenuList(menuList);
		loginUser.setCarrier(this.usrRoleRelService.selectByPrimaryKey(CarrierEntity.class,loginUser.getCarrier()));

		List<TreeMenuVO> treeMenuVOList =initTreeMenuVO(menuList);
		loginUser.setTreeMenuList(treeMenuVOList);
	}

	private List<TreeMenuVO> initTreeMenuVO(List<MenuVO> menuList){
		List<TreeMenuVO> list =new ArrayList<>();
		for(MenuVO menuVO:menuList){
			if("-1".equals(menuVO.getSupMenuId())){
				TreeMenuVO child =new TreeMenuVO(menuVO);
				list.add(child);
				findChild(child,menuList);
			}
		}
        return list;
	}
    private void findChild(TreeMenuVO parent,List<MenuVO> menuList){
	    for(MenuVO menuVO:menuList){
		    if(parent.getMenuId().equals(menuVO.getSupMenuId())){
			    TreeMenuVO child =new TreeMenuVO(menuVO);
			    parent.addChild(child);
			    findChild(child,menuList);
		    }
	    }
    }
}

 
最后,在appctx-shiro.xml里面进行shiro的相关配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
	default-lazy-init="true">

	<description>Shiro安全配置</description>

	<!-- Shiro's main business-tier object for web-enabled applications -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="shiroDbRealm" />
		<property name="cacheManager" ref="shiroMemCachedManager" />
	</bean>

	<!-- 項目自定义的Realm, 所有staffAccountService依赖的dao都需要用depends-on声明 -->
	<bean id="shiroDbRealm" class="com.onlyou.olyfinance.common.shiro.MngRealm">
		<property name="credentialsMatcher">
			<bean class="com.onlyou.olyfinance.common.shiro.RetryLimitCredentialsMatcher">
				<property name="hashAlgorithmName" value="MD5" />
				<property name="group" value="mclient1" />
				<property name="cacheClient" ref="cacheClient" />
			</bean>
		</property>
	</bean>
	
	<!-- Shiro Filter -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/login.enter.htm" />
		<property name="unauthorizedUrl" value="/unauthorized.htm" />
		<property name="successUrl" value="/usr/usrInfo.htm" />
		<property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" />
		<property name="filters">
            <map>
				<entry key="authc">
					<bean id="loginFormAuthenticationFilter" class="com.onlyou.olyfinance.common.shiro.LoginFormAuthenticationFilter">
						<property name="usernameParam" value="username" />
						<property name="passwordParam" value="password" />
						<property name="failureKeyAttribute" value="shiroLoginFailure"/>
					</bean>
				</entry>
				<entry key="jCaptchaValidate">
					<bean class="com.onlyou.olyfinance.common.jcaptcha.JCaptchaValidateFilter">
						<property name="jcaptchaEbabled" value="true" />
						<property name="jcaptchaParam" value="vcode" />
						<property name="failureKeyAttribute" value="shiroLoginFailure" />
						<property name="failureMsg" value="验证码不正确" />
						<property name="errorLimitAttribute" value="errorTimes" />
						<property name="errorLimit" value="3" />
					</bean>
				</entry>
				<entry key="perms">
					<!-- 自定义鉴权拦截器 -->
					<bean id="urlPermissionsFilter" class="com.onlyou.olyfinance.common.shiro.URLPermissionsFilter" />
				</entry>
            </map>
        </property>
	</bean>
	<bean id="chainDefinitionSectionMetaSource"
		class="com.onlyou.olyfinance.common.shiro.MngMetaSource">
		<!-- 默认的连接配置 -->
		<property name="filterChainDefinitions">
			<value>
				#查看应用信息(代码版本,启动时间)
				/getAppInfo.json = anon
				#登出
				/logout = logout

				#不验证资源
				/favicon.ico = anon
				/assets/** = anon
				/unauthorized.htm = anon
				/jcaptcha.jpg* = anon

				#访问这些路径必须拥有某种权限
				/login.enter.htm = jCaptchaValidate,authc

				#其它资源都要求用户
				/** = user,perms
			</value>
		</property>
	</bean>
	
	<!-- 用户授权信息Cache, 采用MemCached -->
	<bean id="shiroMemCachedManager" class="com.onlyou.framework.cache.shiro.MemcachedManagerForShiro">
		<property name="group" value="mclient1" />
		<property name="cacheClient" ref="cacheClient" />
	</bean>
	
	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>

 
 一些shiro的标签应用:

<shiro:authenticated> 登录之后  
 <shiro:notAuthenticated> 不在登录状态时  
<shiro:guest> 用户在没有RememberMe时  
<shiro:user> 用户在RememberMe时  
<shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时  
<shiro:hasRole name="abc"> 拥有角色abc  
<shiro:lacksRole name="abc"> 没有角色abc  
<shiro:hasPermission name="abc"> 拥有权限abc  
<shiro:lacksPermission name="abc"> 没有权限abc  
<shiro:principal> 显示用户登录名

   

 本章只是在应用层面上对shiro配置进行阐述,了解原理可参考以下链接:

http://blog.csdn.net/xiaoyao8903/article/details/53244835

http://jinnianshilongnian.iteye.com/blog/2018936

猜你喜欢

转载自writesblog.iteye.com/blog/2376462
今日推荐