我来又来了,maven+ssm+shiro

由于前天写的有点模糊,我决定推到重来,在做一个demo。

OK!,先空功能实现

一:流程图

gif演示:如下

二:每个任务拥有的角色与权限

一共需要5张表,user,role,user_role,permission,role_permission表格

项目主要用ssm框架搭建,这我就只放置查询语句

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.userDao">
	<select id="getPassword" resultType="String">
		select password from user where name=#{name}
	</select>

	<select id="getRoles" resultType="String">
	select r.name from user u
		left join user_role ur on u.id = ur.uid
		left join Role r on r.id = ur.rid
		where u.name = #{name}
	</select>

	<select id="getPermission" resultType="String">
	select p.name from user u
		left join user_role ru on u.id = ru.uid
		left join role r on r.id = ru.rid
		left join role_permission rp on r.id = rp.rid
		left join permission p on p.id = rp.pid
		where u.name =#{name}
	</select>
//这里新定义了一个user2,用来查询全部信息
	<resultMap type="bean.user2" id="userMap">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="password" column="password"/>
		<collection property="Roles" column="name" select="getRoles"></collection>
		<collection property="Permission" column="name" select="getPermission"></collection>
	</resultMap>
	<select id="getAll" resultMap="userMap">
	select * from user where name=#{name}
	</select>
	
</mapper>

三:配置

OK!!!将ssm框架搭建好之后,就可以开始配置shiro

整体的项目架构如下:

这里pageController的作用:由于jsp文件都放在WEB-INF文件的jsp文件里面

外界无法直接访问,So,需要定义映射去访问它们。pagerController就诞生了,只需要localhost:8080/项目名称/index就可以访问主页。

package controller;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class pageController {
	@RequestMapping(value="index")
	public String index() {
		return "index";
	}
	@RequestMapping(value="login",method=RequestMethod.GET)
	public String login() {
		return "login";
	}
	@RequestMapping(value="cook")
	public String cook() {
		return "cook";
	}
	@RequestMapping(value="driver")
	public String driver() {
		return "driver";
	}
	
	@RequestMapping(value="noRole")
	public String noRole() {
		return "noRole";
	}
	
	@RequestMapping(value="noPermission")
	public String noPermission() {
		return "noPermission";
	}
	@RequestMapping(value="driving")
	public String driving() {
		return "driving";
	}

	@RequestMapping(value="driving2")
	public String driving2() {
		return "driving2";
	}

	@RequestMapping(value="cooking")
	public String cooking() {
		return "cooking";
	}

	@RequestMapping(value="cooking2")
	public String cooking2() {
		return "cooking2";
	}
}

到了这个时候,应该已经可以访问了,当时没有权限控制而已。

四:配置shiro

最简单的Shiro配置

web.xml  配置shiro过滤器

<!-- Shiro配置 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>	
	 

springmvc.xml   配置shiro注释

	<!--启用shiro注解-->
	<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>

spring-shiro.xml 配置过滤器,securityManager等东西

这个配置文件记得加载,在监听器中或者applicationContext.xml都可以

/login    这个指的是login请求  localhost:8080/项目名称/login

/login.jsp  这个访问时jsp页面, maven项目访问webapp下,web项目访问WEB-INF下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util 
	http://www.springframework.org/schema/util/spring-util.xsd">
	
	
	<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 调用我们配置的权限管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 配置我们的登录请求地址 -->
		<property name="loginUrl" value="/login" />
		<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
		<property name="unauthorizedUrl" value="/noPermission" />
		<!-- 退出 -->
		<property name="filters">
			<util:map>
				<entry key="logout" value-ref="logoutFilter" />
			</util:map>
		</property>
		<!-- 权限配置 -->
		<property name="filterChainDefinitions">
			<value>
				<!-- anon表示此地址不需要任何权限即可访问 -->
				/login=anon
				/index=anon
				/static/**=anon
				/doLogout=logout
				<!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
				/** = authc
			</value>
		</property>
	</bean>
	<!-- 退出过滤器 -->
	<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
		<property name="redirectUrl" value="/index" />
	</bean>


	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
		<!--  <property name="sessionManager" ref="sessionManager" />-->
	</bean>

	<bean id="myRealm" class="Realm.myRealm"></bean>
    
	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans> 

五:其他

参数传递的流程:

               登录页面-->Controller,调用验证-->Realm验证 ,验证通过则返回,失败则报错  

               访问有权限的页面->Realm->有权限则通过,没权限则报错

5.1  jsp页面

这个是登录界面<br>
<form action="login" method="post">
用户名:<input type="text" name="name" value="张三" ><br>
密码:<input type="password" name="password" value="123"><br>
<input type="submit" value="提交">
</form>
${error} <!-- 取出错误 -->
<a href="index">返回</a>

5.2Controller接收

@Controller
public class userController {
	@RequestMapping(value="/login",method=RequestMethod.POST)
	public String login(String name,String password,Model model) {
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(name, password);
		try {
                //开始验证
		subject.login(token);
		Session session = subject.getSession();
		session.setAttribute("subject", subject);
		return "index";
		}catch (Exception e){
			model.addAttribute("error", "验证失败");
			return "login";
		}
	}
}

5.3Realm验证

public class myRealm extends AuthorizingRealm {
	@Autowired
	private userService userServiceImpl;
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//取出username
		String name = (String)principals.getPrimaryPrincipal();
		Set<String> Roles = userServiceImpl.getRoles(name);
		Set<String> Permission = userServiceImpl.getPermission(name);
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setRoles(Roles);
		info.setStringPermissions(Permission);
		return info;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken user = (UsernamePasswordToken)token;
		String name = user.getUsername();
		String password = userServiceImpl.getPassword(name);
		if(password==null||!password.equals(new String(user.getPassword()))) {
			throw new AuthenticationException();
		}
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(name, password, getName());
		return info;
	}

}

5.4index 页面在取出

<body>
     //判断有没有验证过
    <c:if test="${empty subject.principal}">
        <a href="login">登录</a><br>
    </c:if>
    <c:if test="${!empty subject.principal}">
        <span class="desc">你好,${subject.principal},</span>
        <a href="doLogout">退出</a><br>
    </c:if>
<a href="cook">炒菜</a><br>
<a href="driver">开车</a>
</body>

这样子登入验证就做好了。

6.权限验证

两种方式:注解,非注解

注解:@RequiresPermissions("驾驶重机车")   要求有“驾驶重机车”的能力  @RequiresRoles("厨师")  要求是厨师角色

好处:简洁

@RequiresPermissions("驾驶重机车")
	@RequestMapping(value="driving")
	public String driving() {
		return "driving";
	}
	@RequiresPermissions("驾驶SUV轿车")
	@RequestMapping(value="driving2")
	public String driving2() {
		return "driving2";
	}
	@RequiresPermissions("蛋炒饭")
	@RequestMapping(value="cooking")
	public String cooking() {
		return "cooking";
	}
	@RequiresPermissions("满汉全席")
	@RequestMapping(value="cooking2")
	public String cooking2() {
		return "cooking2";
	}

坏处:权限不通过的时候,是要出现异常的,报错程序就不运行了,So我们还要手动配置异常统一处理

异常统一处理也有两种:SpringMVC框架自带的,shiro框架自带的

SpringMVC框架:必须写在springmvc.xml中

   <bean  
    class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    <property name="exceptionMappings">  
        <props>  
             //接受到的异常
            <prop key="org.apache.shiro.authz.UnauthorizedException"> 
                /noRole <!-- 捕获该异常时跳转的页面 --> 
            </prop>  
            <prop key="org.apache.shiro.authz.UnauthenticatedException">  
                /noPermission <!-- 捕获该异常时跳转的路径 -->
            </prop>  
        </props>  
    </property>  
</bean> 

shiro框架:它就比较麻烦了,推荐用springmc框架的

 于此不做赘述

非注解:非注解的好处:不需要添加报错后异常处理    坏处:这难道不碍眼么

	@RequestMapping(value="cook")
	public String cook() {
		Subject subject = SecurityUtils.getSubject();
		try {
		List<String> Roles = new ArrayList<String>();
		subject.checkRole("厨师");
		}catch(Exception e) {
			return "noRole";
		}
		return "cook";
	}

最后完整的pageController是这个样子的,两种权限方法都有采用。



@Controller
public class pageController {
	@RequestMapping(value="index")
	public String index() {
		return "index";
	}
	@RequestMapping(value="login",method=RequestMethod.GET)
	public String login() {
		return "login";
	}
	@RequestMapping(value="cook")
	public String cook() {
		Subject subject = SecurityUtils.getSubject();
		try {
		List<String> Roles = new ArrayList<String>();
		subject.checkRole("厨师");
		}catch(Exception e) {
			return "noRole";
		}
		return "cook";
	}
	@RequestMapping(value="driver")
	public String driver() {
		Subject subject = SecurityUtils.getSubject();
		try {
		subject.checkRole("车手");
		}catch(Exception e) {
			return "noRole";
		}
		
		return "driver";
	}
	
	@RequestMapping(value="noRole")
	public String noRole() {
		return "noRole";
	}
	
	@RequestMapping(value="noPermission")
	public String noPermission() {
		return "noPermission";
	}
	@RequiresPermissions("驾驶重机车")
	@RequestMapping(value="driving")
	public String driving() {
		return "driving";
	}
	@RequiresPermissions("驾驶SUV轿车")
	@RequestMapping(value="driving2")
	public String driving2() {
		return "driving2";
	}
	@RequiresPermissions("蛋炒饭")
	@RequestMapping(value="cooking")
	public String cooking() {
		return "cooking";
	}
	@RequiresPermissions("满汉全席")
	@RequestMapping(value="cooking2")
	public String cooking2() {
		return "cooking2";
	}
}

这样子就配置好全部,可以开头的效果。

7.缓存问题

如果在realm的两个方法中加入syso语句,每一次验证或者授权都会输出一条语句,那每一次都会查询数据库。有什么方法可以解决。答案是:缓存

缓存方式有三中:redis(哈哈哈,我不会),EhCache,MpCache.

redis,我不会。

EjCache:实现类,注入SecurityManager

<!-- 缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
    </bean>
 <!--securityManage-->
    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="customRealm" />

        <!--注入缓存管理器-->
        <property name="cacheManager" ref="cacheManager"/>
     </bean>

于此之外,编写shiro-ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!--diskStore:缓存数据持久化的目录 地址  -->
    <diskStore path="/Users/codingboy/develop/ehcache" />
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
 各个属性的作用:数据可以保存到磁盘中或者内存中
      name:缓存名称。
      maxElementsInMemory:缓存最大个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      maxElementsOnDisk:硬盘最大缓存个数。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。       
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。

最后一个:MpCache.老板来个最便宜的,这个就是最便宜的

<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
		<property name="CacheManager" ref="shiroSpringCacheManager"></property>
	</bean>
	
    <bean id = "shiroSpringCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>
    

搞定收工。

8.会话管理

我只想让我的session存在指定的时间。

创建会话管理并注入安全管理器。

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
		<property name="CacheManager" ref="shiroSpringCacheManager"></property>
		<property name="sessionManager" ref="sessionManager"></property>
	</bean>
	

     <!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- session的失效时长,单位毫秒 -->
        <property name="globalSessionTimeout" value="600000"/>
        <!-- 删除失效的session -->
        <property name="deleteInvalidSessions" value="true"/>
    </bean>

猜你喜欢

转载自blog.csdn.net/qq_40008535/article/details/83149047