尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313

最近在学习Shiro,首先非常感谢开涛大神的《跟我学Shiro》系列,在我学习的过程中发挥了很大的指导作用。学习一个新的东西首先就是做一个demo,多看不如多敲,只有在实践中才能发现自己的欠缺,下面记录下来我整合shiro的过程。如果有不足之处,还望各位看官多多指出。

一、基本名词解释

Apache Shiro是一个强大易用的Java安全框架。它可以帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存、单点登录等等,而且它的API也十分简洁易用,所以现在有很多人都在使用它。它的基本能功能点如图所示:

从图上我们可以看出Shiro的四大核心功能:

Authentication(身份验证):简称为“登录”,即证明用户是谁。
Authorization(授权):访问控制的过程,即决定是否有权限去访问受保护的资源。
Session Management(会话管理):管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
Cryptography(加密):通过使用加密算法保持数据安全

我们同时也可以看看Shiro的架构长什么样子:

同样的虚线框框圈着的是Shiro3大核心组件:

Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject  实例都被绑定到(且这是必须的)一个SecurityManager 上。
SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject  进行交互时,实质上是幕后的 SecurityManager  处理所有繁重的 Subject 安全操作。
Realms :本质上是一个特定安全的 DAO。当配置 Shiro  时,必须指定至少一个 Realm  用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm  实现来代表自定义的数据源。

以上是些基本的名称解释。如需要查看更详细的请参考开涛大神的博客。

二、准备工作

整合程序沿用之前的例子Maven+spring+Spring MVC+MyBatis+MySQL整合SSM框架,现在我们需要在此基础上继续整合进Shiro。

三、开始整合

1.加入jar包

整合demo用的是maven对依赖进行管理。我们需要在pom.xml里加上配置:

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.1.32</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.shiro</groupId>
  8. <artifactId>shiro-core</artifactId>
  9. <version>1.2.2</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.apache.shiro</groupId>
  13. <artifactId>shiro-web</artifactId>
  14. <version>1.2.2</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.apache.shiro</groupId>
  18. <artifactId>shiro-spring</artifactId>
  19. <version>1.2.2</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.apache.shiro</groupId>
  23. <artifactId>shiro-ehcache</artifactId>
  24. <version>1.2.2</version>
  25. </dependency>

里面我加入了阿里的fastjson。我用fastjson替换了jackson(理由:没有什么理由,就是喜欢用了.....)

2.Spring-mvc.xml的配置

这是替换掉jackson的配置

  1. <mvc:annotation-driven>
  2. <mvc:message-converters>
  3. <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
  4. <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
  5. <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
  6. <!--<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />-->
  7. <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" />
  8. </mvc:message-converters>
  9. </mvc:annotation-driven>
  10. <mvc:default-servlet-handler />
  11. <!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
  12. <!-- 支持JSON数据格式 -->
  13. <bean
  14. class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  15. <property name="messageConverters">
  16. <list>
  17. <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/><!-- 解析导出文件byte流 -->
  18. <ref bean="fastJsonHttpMessageConverter" />
  19. <!--
  20. <ref bean="mappingJacksonHttpMessageConverter" />
  21. -->
  22. </list>
  23. </property>
  24. </bean>
  25. <!--<bean id="mappingJacksonHttpMessageConverter"
  26. class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
  27. </bean>-->
  28. <!-- 使用fastJson来支持JSON数据格式 -->
  29. <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
  30. <property name="supportedMediaTypes">
  31. <list>
  32. <value>text/html;charset=UTF-8</value>
  33. <value>application/json</value>
  34. </list>
  35. </property>
  36. <property name="features">
  37. <list>
  38. <value>WriteMapNullValue</value>
  39. <value>QuoteFieldNames</value>
  40. </list>
  41. </property>
  42. </bean>

3.首先先配置Shiro Filter(web.xml)

  1. <!-- 配置  Shiro 的 Filter -->
  2. <filter>
  3. <description>shiro 权限拦截</description>
  4. <filter-name>shiroFilter</filter-name>
  5. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  6. <init-param>
  7. <param-name>targetFilterLifecycle</param-name>
  8. <param-value>true</param-value>
  9. </init-param>
  10. </filter>
  11. <filter-mapping>
  12. <filter-name>shiroFilter</filter-name>
  13. <url-pattern>/*</url-pattern>
  14. </filter-mapping>

4.创建Shiro的配置文件spring-shiro(注:这边统一起名为spring-*.xml,然后可以在配置contextConfigLocation时使用通配符了)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
  5. <!-- 启用shrio授权注解拦截方式 -->
  6. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  7. <!-- 装配 securityManager -->
  8. <property name="securityManager" ref="securityManager"/>
  9. <!-- 配置登陆页面 -->
  10. <property name="loginUrl" value="/index.jsp"/>
  11. <!-- 登陆成功后的一面 -->
  12. <property name="successUrl" value="/jsp/success.jsp"/>
  13. <span style="white-space:pre">    </span><property name="unauthorizedUrl" value="/jsp/unauthorized.jsp"/>
  14. <!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截.  -->
  15. <property name="filterChainDefinitions">
  16. <value>
  17. /index.jsp=anon
  18. /jsp/success.jsp=anon
  19. /jsp/fail.jsp=anon
  20. /jsp/user.jsp = roles[user]
  21. /jsp/admin.jsp = roles[admin]
  22. /logout = logout
  23. </value>
  24. </property>
  25. </bean>
  26. <!-- 配置缓存管理器 -->
  27. <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
  28. <!-- 指定 ehcache 的配置文件 -->
  29. <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
  30. </bean>
  31. <!-- 配置进行授权和认证的 Realm -->
  32. <bean id="myRealm" class="com.gray.base.shiro.ShiroDbRealm">
  33. <property name="userService" ref="userService" />
  34. </bean>
  35. <bean id="userService" class="com.gray.user.service.impl.UserServiceImpl" />
  36. <!-- 配置 Shiro 的 SecurityManager Bean. -->
  37. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  38. <property name="cacheManager" ref="cacheManager"/>
  39. <property name="realm" ref="myRealm"/>
  40. <property name="sessionMode" value="native">
  41. </property>
  42. </bean>
  43. <!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
  44. <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
  45. </beans>

5.数据库及页面设计好了。以上就是所有整合Shiro所需要配置的东西,下面我们开始写程序,为了方便,数据库我沿用之前的user表,将它进行了小小的改造,在实际项目中对角色和权限的控制需要在定义多张表的。helloworld程序就将就着用了。sql语句如下:CREATE TABLE `user` (`username` varchar(40) NOT NULL,`password` varchar(40) NOT NULL,`age` int(3) DEFAULT NULL,`name` varchar(40) DEFAULT NULL,`role` varchar(40) DEFAULT NULL,PRIMARY KEY (`username`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;表结构定义完后需要写实体类,这里就不贴代码了。jsp页面如图所示: success.jsp在这里我加入了Shiro的标签来控制页面的显示。其他的jsp就不一一列出代码了。

  1. <%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%>
  2. <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  4. <html>
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  7. <title></title>
  8. </head>
  9. <body>
  10. ${successMsg } Welcome!  <shiro:principal/>
  11. <br><br>
  12. <shiro:hasAnyRoles name="user">
  13. <a href="/jsp/user.jsp">User Page</a>
  14. </shiro:hasAnyRoles>
  15. <br><br>
  16. <shiro:hasAnyRoles name="admin">
  17. <a href="/jsp/admin.jsp">Admin Page</a>
  18. </shiro:hasAnyRoles>
  19. <br><br>
  20. <a href="../test/logout.do">Logout</a>
  21. </body>
  22. </html>

6.后台代码的编写
6.1 Controller层的编写
这次整合我么主要要实现登陆和登出功能,因此controller层我们这样写:

  1. package com.gray.user.controller;
  2. import java.io.IOException;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import org.apache.shiro.SecurityUtils;
  6. import org.apache.shiro.authc.AuthenticationException;
  7. import org.apache.shiro.authc.IncorrectCredentialsException;
  8. import org.apache.shiro.authc.UnknownAccountException;
  9. import org.apache.shiro.authc.UsernamePasswordToken;
  10. import org.apache.shiro.subject.Subject;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Controller;
  13. import org.springframework.ui.Model;
  14. import org.springframework.web.bind.annotation.RequestMapping;
  15. import com.gray.user.entity.User;
  16. import com.gray.user.service.impl.UserServiceImpl;
  17. @Controller
  18. @RequestMapping("/test")
  19. public class LoginController {
  20. @Autowired
  21. private UserServiceImpl userService;
  22. @RequestMapping("/dologin.do") //url
  23. public String dologin(User user, Model model){
  24. String info = loginUser(user);
  25. if (!"SUCC".equals(info)) {
  26. model.addAttribute("failMsg", "用户不存在或密码错误!");
  27. return "/jsp/fail";
  28. }else{
  29. model.addAttribute("successMsg", "登陆成功!");//返回到页面说夹带的参数
  30. model.addAttribute("name", user.getUsername());
  31. return "/jsp/success";//返回的页面
  32. }
  33. }
  34. @RequestMapping("/logout.do")
  35. public void logout(HttpServletRequest request,HttpServletResponse response) throws IOException{
  36. Subject subject = SecurityUtils.getSubject();
  37. if (subject != null) {
  38. try{
  39. subject.logout();
  40. }catch(Exception ex){
  41. }
  42. }
  43. response.sendRedirect("/index.jsp");
  44. }
  45. private String loginUser(User user) {
  46. if (isRelogin(user)) return "SUCC"; // 如果已经登陆,无需重新登录
  47. return shiroLogin(user); // 调用shiro的登陆验证
  48. }
  49. private String shiroLogin(User user) {
  50. // 组装token,包括客户公司名称、简称、客户编号、用户名称;密码
  51. UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword().toCharArray(), null);
  52. token.setRememberMe(true);
  53. // shiro登陆验证
  54. try {
  55. SecurityUtils.getSubject().login(token);
  56. } catch (UnknownAccountException ex) {
  57. return "用户不存在或者密码错误!";
  58. } catch (IncorrectCredentialsException ex) {
  59. return "用户不存在或者密码错误!";
  60. } catch (AuthenticationException ex) {
  61. return ex.getMessage(); // 自定义报错信息
  62. } catch (Exception ex) {
  63. ex.printStackTrace();
  64. return "内部错误,请重试!";
  65. }
  66. return "SUCC";
  67. }
  68. private boolean isRelogin(User user) {
  69. Subject us = SecurityUtils.getSubject();
  70. if (us.isAuthenticated()) {
  71. return true; // 参数未改变,无需重新登录,默认为已经登录成功
  72. }
  73. return false; // 需要重新登陆
  74. }
  75. }

6.2 实现自定义Realm
shiro从Realm获取安全数据,也就是说SecurityManager要验证身份,它需要从Realm获取相应的用户进行比较以确定用户的身份是否合法;我们可以把Realm看作是DataSource,安全数据源。实现自定义Realm主要是继承AuthrizingRealm这个父类,重写doGetAuthrizationInfo和doGetAuthenticationInfo这两个方法,其中doGetAuthenticationInfo是用来验证用户合法性的,根据输入的用户信息从数据库中查出用户,根据用户情况抛出不同的异常。doGetAuthrizationInfo是对当前用的用户进行授权的。在这里我把角色写死了。在实际项目开发中应该是在表中做权限和角色的定义再从数据库中查出一个集合然后迭代授权。具体实现代码如下:

  1. package com.gray.base.shiro;
  2. import org.apache.shiro.SecurityUtils;
  3. import org.apache.shiro.authc.AuthenticationException;
  4. import org.apache.shiro.authc.AuthenticationInfo;
  5. import org.apache.shiro.authc.AuthenticationToken;
  6. import org.apache.shiro.authc.SimpleAuthenticationInfo;
  7. import org.apache.shiro.authc.UsernamePasswordToken;
  8. import org.apache.shiro.authz.AuthorizationInfo;
  9. import org.apache.shiro.authz.SimpleAuthorizationInfo;
  10. import org.apache.shiro.realm.AuthorizingRealm;
  11. import org.apache.shiro.session.Session;
  12. import org.apache.shiro.subject.PrincipalCollection;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import com.gray.user.entity.User;
  15. import com.gray.user.service.impl.UserServiceImpl;
  16. public class ShiroDbRealm extends AuthorizingRealm {
  17. @Autowired
  18. private UserServiceImpl userService;
  19. public static final String SESSION_USER_KEY = "gray";
  20. /**
  21. * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法
  22. */
  23. @Override
  24. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
  25. User user = (User) SecurityUtils.getSubject().getSession().getAttribute(ShiroDbRealm.SESSION_USER_KEY);
  26. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  27. info.addRole(user.getRole().trim());
  28. return info;
  29. }
  30. /**
  31. * 认证回调函数,登录信息和用户验证信息验证
  32. */
  33. @Override
  34. protected AuthenticationInfo doGetAuthenticationInfo(
  35. AuthenticationToken authcToken) throws AuthenticationException {
  36. // 把token转换成User对象
  37. User userLogin = tokenToUser((UsernamePasswordToken) authcToken);
  38. // 验证用户是否可以登录
  39. User ui = userService.doUserLogin(userLogin);
  40. if(ui == null)
  41. return null; // 异常处理,找不到数据
  42. // 设置session
  43. Session session = SecurityUtils.getSubject().getSession();
  44. session.setAttribute(ShiroDbRealm.SESSION_USER_KEY, ui);
  45. //当前 Realm 的 name
  46. String realmName = this.getName();
  47. //登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
  48. //      Object principal = ui.getUsername();
  49. Object principal = authcToken.getPrincipal();
  50. return new SimpleAuthenticationInfo(principal, userLogin.getPassword(), realmName);
  51. }
  52. private User tokenToUser(UsernamePasswordToken authcToken) {
  53. User user = new User();
  54. user.setUsername(authcToken.getUsername());
  55. user.setPassword(String.valueOf(authcToken.getPassword()));
  56. return user;
  57. }
  58. //一定要写getset方法
  59. public UserServiceImpl getUserService() {
  60. return userService;
  61. }
  62. public void setUserService(UserServiceImpl userService) {
  63. this.userService = userService;
  64. }
  65. }

在这里我们要注意的是用注解没办法注入我们要的service,一定要写getset方法,不然会报错(网上的解决办法挺有道理的但是试过没效果。。。。)。
具体的serviceImpl的实现我就不写出来了,数据库的查询我相信大家都会(默认你们都会)。随后如果有需要我会把整合代码放上来。
最后贴几张测试图:
登陆界面:

以管理员角色的账号登陆:

AdminPage:

以普通用户角色的账号登陆:

UserPage:

当你以普通用户登录却想访问adminpage时:

登陆失败:

最后登出点击logout返回登陆主界面。
以上就是整合Shiro的所有过程,当然在实际项目中不会那么简单但我觉得原理也是差不多的。学无止境,最后再次感谢网上提供资料的各位大神。

SpringMVC整合Shiro权限框架的更多相关文章

  1. SpringBoot整合Shiro权限框架实战

    什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...

  2. SpringMVC整合Shiro安全框架(一)

    一. 准备工作 1. 本文参考自张开涛的 <跟我学Shiro> 二. 简介 1. Apache Shiro是Java的一个安全框架.可以帮助我们完成:认证.授权.加密.会话管理.与Web集 ...

  3. Spring Boot:整合Shiro权限框架

    综合概述 Shiro是Apache旗下的一个开源项目,它是一个非常易用的安全框架,提供了包括认证.授权.加密.会话管理等功能,与Spring Security一样属基于权限的安全框架,但是与Sprin ...

  4. (转) shiro权限框架详解06-shiro与web项目整合(上)

    http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...

  5. SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能

    SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...

  6. SpringMVC整合Shiro——(3)

    SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...

  7. Shiro权限框架简介

    http://blog.csdn.net/xiaoxian8023/article/details/17892041   Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论 ...

  8. 在前后端分离的SpringBoot项目中集成Shiro权限框架

    参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制   以及跨域的问题也有涉及

  9. Spring整合Shiro 权限 角色 用户关系分析

    Spring整合Shiro 权限 角色 用户关系分析 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 前置内容 之前我们学习了,使用注解的方式去完成权限的控制,当然,也是静态的,也就 ...

随机推荐

  1. Nginx 错误处理方法: bind() to 0.0.0.0:80 failed

    Nginx 错误处理方法: bind() to 0.0.0.0:80 failed 今天启动window上的nginx总是报错 错误信息是bind() to 0.0.0.0:80 failed (10 ...

  2. 5.Nginx作为web缓存服务器

    Nginx作为web缓存服务器 从0.7.48版本开始,Nginx支持类似Squid的缓存功能.Nginx的web缓存服务主要由proxy_cache相关命令集合fastcgi_cache相关命令集构 ...

  3. C#的发展已经15年了 。。。历史发展

    C#是微软公司在2000年6月发布的一种新的编程语言,主要由安德斯·海尔斯伯格(Anders Hejlsberg)主持开发,它是第一个面向组件的编程语言,其源码会编译成msil再运行.它借鉴了Delp ...

  4. MySQL小抄

    以下是MySQL5.7中的一些tips&tricks(持续更新中): Use of an unqualified * with other items in the select list m ...

  5. NodeJS初介

    之前很多环境搭建中都使用到了Nodejs,所以这边对Nodejs做一个简单总结. 1.什么是Nodejs Node.js是一个Javascript运行环境(runtime),发布于2009年5月,由R ...

  6. 腾讯WeTest发布《2017中国移动游戏质量白皮书》,专注手游品质提升

    1月8日,腾讯质量开放平台WeTest正式发布<2017中国移动游戏质量白皮书>. 刚刚过去的这一年,市场逐渐成熟,中国移动互联网由增量市场转向存量市场.中国移动游戏市场急剧变化,真正的精 ...

  7. Vista 及后续版本的新线程池

    在上一篇的博文中,说了下老版本的线程池,在Vista之后,微软重新设计了一套线程池机制,并引入一组新的线程池API,新版线程池相对于老版本的来说,它的可控性更高,它允许程序员自己定义线程池,并规定线程 ...

  8. CSS中的块级元素(block)与行内元素(inline)

    css中有3种基本的定位机制:普通流(相对定位实际上看做普通流定位模型的一部分)浮动(float)绝对定位(固定定位是绝对定位的一种)所以在学习浮动之前,我们先要了解块级元素与内联元素(行内元素).块 ...

  9. 做了一个web版的 MyBatis Generator

    mybatis 官方提供了 MyBatis Generator ,可以通过 xml 配置文件的方式使用,例如自己写调用脚本,或者使用 mvn 插件的方式,其实实现起来还是很简单的.虽然简单,但还是不够 ...

  10. 【整理】REACT一些自己感觉需要记的东西

    REACT生命周期: 组件的生命周期可分成三个状态: Mounting:已插入真实 DOM Updating:正在被重新渲染 Unmounting:已移出真实 DOM 生命周期的方法有: compon ...