SSM框架_3(shiro权限管理)

2017-05-01(采用shiro,实现用户的登陆与权限控制等)

一、添加spring-shiro.xml和ehcache-shiro.xml

spring-shiro.xml:shiro主配置文件html

<?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>

	<!-- 項目自定义的Realm -->
	<bean id="userShiroRealm" class="com.ssm.shiro.UserShiroRealm" />

	<!--安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!--设置自定义Realm -->
		<property name="realm" ref="userShiroRealm" />
		<!--将缓存管理器,交给安全管理器 -->
		<property name="cacheManager" ref="shiroEhcacheManager" />
	</bean>

	<!-- Shiro Filter -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 安全管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 默认的登录访问url -->
		<property name="loginUrl" value="/manage/login" />
		<!-- 没有权限跳转的url -->
		<property name="unauthorizedUrl" value="/unauth"/>
		<!-- 自定义filter配置 -->
		<property name="filters">
			<map>
				<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
			</map>
		</property>
		<property name="filterChainDefinitions">
			<value>
				/manage/login = anon<!-- 不须要认证 能够理解为匿名用户或游客 -->
				/manage/loginPost = anon<!-- 不须要认证 能够理解为匿名用户或游客 -->
				/guest/** = anon
				/manage/** = authc
			</value>
		</property>
	</bean>

	<!-- 用户受权信息Cache, 采用EhCache -->
	<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:conf/shiro/ehcache-shiro.xml" />
	</bean>

	<!-- 在方法中 注入 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内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

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

	<!-- 启用shrio受权注解拦截方式 -->
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>
</beans>

ehcache-shiro.xml:Shrio缺省提供了基于ehCache来缓存用户认证信息和受权信息的实现,该xml为指定ehCache的配置文件前端

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
	<defaultCache maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false"
		diskPersistent="false" diskExpiryThreadIntervalSeconds="120" />
</ehcache>

二、添加ShiroUser.java和UserShiroRealm.java

ShiroUser.java:自定义Authentication对象,使得Subject除了携带用户的登陆名外还能够携带更多信息java

package com.ssm.shiro;

import lombok.Data;
import lombok.Getter;

import java.io.Serializable;

/**
 * @description:自定义Authentication对象,使得Subject除了携带用户的登陆名外还能够携带更多信息
 */
@Data
public class ShiroUser implements Serializable {

    private static final long serialVersionUID = -1373760761780840081L;
    public String userId;
    public String userName;

    public ShiroUser( String userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

}

UserShiroRealm.java:经过UserShiroRealm 继承Shiro验证用户登陆的类为自定义的AuthorizingRealmweb

package com.ssm.shiro;

import com.ssm.model.User;
import com.ssm.service.UserService;
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 java.util.*;

/**
 * @description:shiro权限认证
 * 经过UserShiroRealm 继承Shiro验证用户登陆的类为自定义的AuthorizingRealm 
 */
public class UserShiroRealm extends AuthorizingRealm {

    private static Logger LOGGER = LoggerFactory.getLogger(UserShiroRealm.class);

    @Autowired
    private UserService userService;

    /**
     * 统一用户登陆入口
     * Shiro登陆认证(原理:用户提交 用户名和密码  --- shiro 封装令牌 ---- realm 经过用户名将密码查询返回 ---- 
     * shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登录控制 )
     * 
     * 其中密码加密方式:md5 32为加密
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        LOGGER.info("Shiro开始登陆认证");
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        User user = userService.findUserByUserId(token.getUsername());
        // 帐号不存在
        if (user == null) {
            return null;
        }
        ShiroUser shiroUser = new ShiroUser(user.getId(), user.getUserName());
        // 认证缓存信息
        return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(), getName());
    }
    /**
     * Shiro权限认证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        Set<String> urlSet = new HashSet<String>();
        urlSet.add("/manage/user");
        urlSet.add("/manage/user/user");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(urlSet);
        return info;

    }
    
    /**
     * 清除权限的缓存,但该方法执行后,前端页面的刷新会略有延迟,这是缓存形成的
     * 
     * @author jiangCaiJun
     */
    public void updateAuthz(Long roleId){
        Set<String> urlSet = new HashSet<String>();
        List<Map<String, String>>  roleResourceList= new ArrayList<>();
        if (roleResourceList.size() >0) {
            for (Map<String, String> map : roleResourceList) {
            	if(map != null){
            		urlSet.add(map.get("user"));
            	}
            }
        }
        //注意:此处模拟塞入资源权限对应的标签,实际项目中,应从对应的资源与权限关系表中获取
        urlSet.add("/manage/user");
        urlSet.add("/manage/user/user");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(urlSet);
    }
    
}

二、登陆方法login

前台ajax请求,传递username和password过来,封装成token,进行登陆校验ajax

@RequestMapping(value = "/loginPost", method = RequestMethod.POST)
    @ResponseBody
    public Object loginPost(String username, String password) {
        if (StringUtils.isBlank(username)) {
            return renderError("用户名不能为空");
        }
        if (StringUtils.isBlank(password)) {
            return renderError("密码不能为空");
        }
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            subject.login(token);
        } catch (UnknownAccountException e) {
            logger.error("帐号不存在:{}", e);
            return renderError("帐号不存在");
        } catch (DisabledAccountException e) {
            logger.error("帐号未启用:{}", e);
            return renderError("帐号未启用");
        } catch (IncorrectCredentialsException e) {
            logger.error("密码错误:{}", e);
            return renderError("密码错误");
        } catch (ExcessiveAttemptsException e) {
            logger.error("登陆失败屡次,帐户锁定:{}", e);
            return renderError("登陆失败屡次,帐户锁定10分钟");
        } catch (RuntimeException e) {
            logger.error("未知错误,请联系管理员:{}", e);
            return renderError("未知错误,请联系管理员");
        }
        return renderSuccess();
    }

三、登陆的实现

登陆页面以下spring

登陆页面

输入正确或错误的用户名和密码,返回分别以下apache

登陆_成功

登陆_失败

四、shiro的权限控制

4.1 回到UserShiroRealm.java,向set中塞入了该user有权限访问的url请求地址。此处模拟塞入了 "/manage/user"和"/manage/user/user"。

/**
     * Shiro权限认证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        Set<String> urlSet = new HashSet<String>();
        urlSet.add("/manage/user");
        urlSet.add("/manage/user/user");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(urlSet);
        return info;

    }

4.2 修改UserController.java方法:利用shiro的RequiresPermissions注解,只有在上一步成功添加了对应的url请求地址,才能请求该url

@Controller
@RequestMapping("/manage/user")
@RequiresPermissions(value = { "/manage/user" })
public class UserController {
	private static final Logger LOG = Logger.getLogger(UserController.class);

 	@Autowired
	private UserService userService;

	/**
	 * 用户页
	 *
	 * @return
	 */
	@RequiresPermissions(value = { "/manage/user/user" })
	@RequestMapping(value = "/user", method = RequestMethod.GET)
	public String userManager(Model model) {
		String id = "1";
		User user = userService.getUser(id);
		model.addAttribute("user", user);
		LOG.info(user.toString());
		return "user/showUser";
	}
}

4.3 html中的shiro:hasPermission标签

利用<shiro:hasPermission name="/manage/user/user"></shiro:hasPermission">来显示或隐藏<strong> /manage/user/user</strong>,若没有显示,则证实还没有拥有该权限。缓存

<shiro:hasPermission name="/manage/user/user">
    <span>此处权限控制</span>
	<a href="${staticPath }/manage/user/user">
		${staticPath }/manage/user/user
	</a>
</shiro:hasPermission>

注意:要在页面上添加shiro标签安全

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

该页面的信息以下图:mybatis

首页

参考连接:Apache Shiro 使用手册(三)Shiro 受权 - kdboy - ITeye技术网站

具体信息以下图:

首页

五、maven + spring + spring MVC + mybatis + shiro 项目项目结构图

maven + spring + spring MVC + mybatis + shiro 项目项目结构图

相关文章
相关标签/搜索