shiro框架示例--spring与shiro框架的整合

一 maven添加jar包依赖

        <!-- shiro -->
        <!-- shiro整合spring jar包 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- shiro的缓存jar包依赖 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- shiro核心jar包 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- shiro-web -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- shiro定时任务 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>${shiro.version}</version>
        </dependency>


二 在web.xml中添加shiroFilter过滤器

<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	<async-supported>true</async-supported>
	<init-param>
		<param-name>targetFilterLifecycle</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>

三  配置spring文件

在applicationContext文件中添加下面这句

 <!-- Shiro安全框架产生代理子类的方式: 使用cglib方式,放在事务管理器之前 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

四 配置applicationContext-shiro.xml

将shiro配置单独从applicationContext分离出来,单独配置这个文件,更清爽

   <description>apache shiro配置</description>
   <!-- filter-name这个名字的值来自于web.xml中filter的名字 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 登陆页面配置 -->
        <property name="loginUrl" value="/views/login.html"/>
        <!-- 登陆成功首页面 -->
        <property name="successUrl" value="/views/index.html"/>
        <!-- 未经授权的跳转的url -->
        <property name="unauthorizedUrl" value="/rest/page/401"/>
        <!-- 过滤器链定义 -->        
        <property name="filterChainDefinitions">
            <value>
            <!-- anno,任何人都可以访问;authc:必须是登录之后才能进行访问, -->
                <!-- 静态资源允许访问 -->
                /app/** = anon
                /assets/** = anon
                /plugins/** = anon
                <!-- 登录页允许访问 -->
                /rest/user/login = anon
                /rest/authz/login = anon
                <!-- app接口允许访问 -->
                /rest/app/** = anon
                <!-- 其他资源需要认证 -->
                /** = authc
            </value>
        </property>
        <property name="filters">
        	<map>
                <entry key="authc" value-ref="myFormAuthenticationFilter"/>
            </map>
        </property>
    </bean>
    
   <!--自定义的过滤器 -->
    <bean id="myFormAuthenticationFilter" class="com.yufei.core.feature.orm.myFormAuthenticationFilter" />

 <!--Shiro正是通过CacheManager组件实现权限数据缓存。
当权限信息存放在数据库中时,对于每次前端的访问请求都需要进行一次数据库查询。
特别是在大量使用shiro的jsp标签的场景下,对应前端的一个页面访问请求会同时出现很多的权限查询操作,
这对于权限信息变化不是很频繁的场景,每次前端页面访问都进行大量的权限数据库查询是非常不经济的。因此,非常有必要对权限数据使用缓存方案。-->
    <!-- 缓存管理器 使用Ehcache实现 -->
    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
    </bean>

    <!-- 会话DAO -->
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO"/>

    <!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">    	
        <property name="sessionDAO" ref="sessionDAO"/>
        
        <!-- 会话超时时间,单位:3600000毫秒 =1小时 -->
        <property name="globalSessionTimeout" value="1800000"/>
        
        <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->
        <property name="sessionValidationInterval" value="60000"/>
<!--          <property name="sessionValidationSchedulerEnabled" value="false"/> -->
         <property name="sessionValidationSchedulerEnabled" value="true"/>
         
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
        <property name="sessionIdCookieEnabled" value="true"/>
    </bean>
    
    <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
        	当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
    <!--通过配置sessionIdCookie属性,解决被服务器重写cookie中会话ID -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg name="name" value="jeesite.session.id"/>
    </bean>

    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realms">
            <list>
            <!-- 自定义的realm -->
                <ref bean="securityRealm"/>
            </list>
        </property>
        <!-- cacheManager,集合spring缓存工厂 -->
        <property name="cacheManager" ref="shiroEhcacheManager" />
        <property name="sessionManager" ref="sessionManager" />
    </bean>

    <!-- Shiro生命周期处理器 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

</beans>

在上面的配置文件上有几个地方注意:

1.过滤器配置里的自定义的过滤器myFormAuthenticationFilter

public class myFormAuthenticationFilter extends FormAuthenticationFilter {
//过滤器
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		if (isLoginRequest(request, response)) {
			if (isLoginSubmission(request, response)) {
				return executeLogin(request, response);
			} else {

				return true;////表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理
			}
		} else {
			HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//	        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//	        httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/rest/page/login");
	        response.getWriter().println("<script>window.open('" + httpServletRequest.getContextPath() + "_ui/views/login.html' , '_top')</script>");
			return false;//如果返回false表示该拦截器实例已经处理了,跳转到登录界面。
		}
	}

}

2.缓存管理器的导入ehcache-shiro.xml

<ehcache updateCheck="false" name="shiroCache">

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
</ehcache>
3.自定义的securityRealm
@Component(value = "securityRealm")
public class SecurityRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Resource
    private RoleService roleService;

    /**
     * 权限检查
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    	
    	  //principals:身份,即主体的标识属性,
    	 
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String username = String.valueOf(principals.getPrimaryPrincipal());//一个主体可以有多个属性但只有一个Primary principals,这里指的是用户名

        MySysDeptPerson user = null;
		try {
			user = userService.selectByUsername(username);//通过获取的username查询数据库中是否有对应的用户名
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (user == null){
			return null;
		}
		//如果用户名不为空,则通过用户id查询出用户角色
        final List<SysRole> roleInfos = roleService.selectRolesByUserId(user.getPersonId());
        for (SysRole role : roleInfos) {
            // 添加角色
            System.err.println(role);
            authorizationInfo.addRole(role.getRoleCode());
        }
        return authorizationInfo;//返回权限信息
    }

    /**
     * 登录验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
         //token:登录令牌
    	String username = String.valueOf(token.getPrincipal());//获取用户名
        String password = new String((char[]) token.getCredentials());//获取密码
        // 通过数据库进行验证
        final SysPerson authentication = userService.authentication(new SysPerson(username, password));
        if (authentication == null) {
            throw new AuthenticationException("用户名或密码错误.");
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, getName());//getName指的是当前realm的名称
        return authenticationInfo;
    }

}

五 在web.xml引入,项目启动部署部署

<context-param>
    <param-name>contextConfigLocation</param-name>
	<param-value>
            classpath*:applicationContext.xml
            classpath*:applicationContext-shiro.xml
        </param-value>
</context-param>

上面部署工作完成,开始写自己的代码

controller层

	/**
	 * 用户【登录】
	 * @param user
	 * @param result
	 */
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	/*
	 *@Valid  数据校验
	 *BindingResult 错误信息 跟@Valid一起 
	 */
	public String login(@Valid SysPerson user, String sessionTimeout, BindingResult result, Model model, HttpServletRequest request) throws Exception {
		try {
			// 获取登录信息
			Subject subject = SecurityUtils.getSubject();
			// 已登陆则 跳到首页
			if (subject.isAuthenticated()) {
			//一个被验证的Subject是成功验证后(如登录成功)并存于当前session中,一个被认为验证过的对象调用subject.isAuthenticated()将返回真。
				return "index";
			}
			// 参数错误
			if (result.hasErrors()) {
				model.addAttribute("error", "参数错误!");
				return "login";
			}
			// 参数未传
			if (StringUtils.isBlank(user.getUserName()) && StringUtils.isBlank(user.getPassword())) {
				return "login";// 跳回登录页
			}
			// 1.身份验证
			subject.login(new UsernamePasswordToken(user.getUserName(), user.getPassword()));
			// 验证成功在Session中保存用户信息
			final MySysDeptPerson authUserInfo = userService.selectByUsername(user.getUserName());

			
			// 2.如果登录的是内置用户则看到的是所有单位和部门,非内置用户需要根据权限筛选
			if (authUserInfo.getIsinner() == AdminUserType.NOTINNERUSER.getValue()) {
                 /**
                  * 这里面的根据自己的需求来写,我就不放上去了
                  */
			}
			// 3.在Session中保存用户、角色信息
			List<SysRole> roleList = roleService.selectRolesByUserId(authUserInfo.getPersonId());
			request.getSession().setAttribute("userInfo", authUserInfo);
			request.getSession().setAttribute("roleList", roleList);

			// 4.是否超时
			if (sessionTimeout != null) {
				SecurityUtils.getSubject().getSession().setTimeout(Long.parseLong(sessionTimeout));
			}
			// 5.记录到操作日志
			this.saveOperatorLog("首页", "用户登陆:" + authUserInfo.getName());//这个方法是继承BaseController中的方法
		} catch (AuthenticationException e) {
			// 身份验证失败
			model.addAttribute("error", "用户名或密码错误 !");
			return "login";
		}
		return "redirect:/";
	}
/**
	 * 用户【登出】
	 * @param session
	 * @return
	 */
	@RequestMapping(value = "/logout", method = RequestMethod.GET)
	public String logout(HttpSession session) {
		session.removeAttribute("userInfo");//移除session中的用户信息
		// 登出操作
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return "login";
	}






扫描二维码关注公众号,回复: 2258325 查看本文章





猜你喜欢

转载自blog.csdn.net/qq_33223299/article/details/80825857
今日推荐