SpringMVC整合Shiro权限框架
尊重原创: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里加上配置:
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.1.32</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-core</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-web</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-spring</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-ehcache</artifactId>
- <version>1.2.2</version>
- </dependency>
里面我加入了阿里的fastjson。我用fastjson替换了jackson(理由:没有什么理由,就是喜欢用了.....)
2.Spring-mvc.xml的配置
这是替换掉jackson的配置
- <mvc:annotation-driven>
- <mvc:message-converters>
- <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
- <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
- <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
- <!--<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />-->
- <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" />
- </mvc:message-converters>
- </mvc:annotation-driven>
- <mvc:default-servlet-handler />
- <!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
- <!-- 支持JSON数据格式 -->
- <bean
- class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
- <property name="messageConverters">
- <list>
- <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/><!-- 解析导出文件byte流 -->
- <ref bean="fastJsonHttpMessageConverter" />
- <!--
- <ref bean="mappingJacksonHttpMessageConverter" />
- -->
- </list>
- </property>
- </bean>
- <!--<bean id="mappingJacksonHttpMessageConverter"
- class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
- </bean>-->
- <!-- 使用fastJson来支持JSON数据格式 -->
- <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
- <property name="supportedMediaTypes">
- <list>
- <value>text/html;charset=UTF-8</value>
- <value>application/json</value>
- </list>
- </property>
- <property name="features">
- <list>
- <value>WriteMapNullValue</value>
- <value>QuoteFieldNames</value>
- </list>
- </property>
- </bean>
3.首先先配置Shiro Filter(web.xml)
- <!-- 配置 Shiro 的 Filter -->
- <filter>
- <description>shiro 权限拦截</description>
- <filter-name>shiroFilter</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- <init-param>
- <param-name>targetFilterLifecycle</param-name>
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>shiroFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
4.创建Shiro的配置文件spring-shiro(注:这边统一起名为spring-*.xml,然后可以在配置contextConfigLocation时使用通配符了)
- <?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.1.xsd">
- <!-- 启用shrio授权注解拦截方式 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <!-- 装配 securityManager -->
- <property name="securityManager" ref="securityManager"/>
- <!-- 配置登陆页面 -->
- <property name="loginUrl" value="/index.jsp"/>
- <!-- 登陆成功后的一面 -->
- <property name="successUrl" value="/jsp/success.jsp"/>
- <span style="white-space:pre"> </span><property name="unauthorizedUrl" value="/jsp/unauthorized.jsp"/>
- <!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截. -->
- <property name="filterChainDefinitions">
- <value>
- /index.jsp=anon
- /jsp/success.jsp=anon
- /jsp/fail.jsp=anon
- /jsp/user.jsp = roles[user]
- /jsp/admin.jsp = roles[admin]
- /logout = logout
- </value>
- </property>
- </bean>
- <!-- 配置缓存管理器 -->
- <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
- <!-- 指定 ehcache 的配置文件 -->
- <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
- </bean>
- <!-- 配置进行授权和认证的 Realm -->
- <bean id="myRealm" class="com.gray.base.shiro.ShiroDbRealm">
- <property name="userService" ref="userService" />
- </bean>
- <bean id="userService" class="com.gray.user.service.impl.UserServiceImpl" />
- <!-- 配置 Shiro 的 SecurityManager Bean. -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="cacheManager" ref="cacheManager"/>
- <property name="realm" ref="myRealm"/>
- <property name="sessionMode" value="native">
- </property>
- </bean>
- <!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
- </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就不一一列出代码了。
- <%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%>
- <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title></title>
- </head>
- <body>
- ${successMsg } Welcome! <shiro:principal/>
- <br><br>
- <shiro:hasAnyRoles name="user">
- <a href="/jsp/user.jsp">User Page</a>
- </shiro:hasAnyRoles>
- <br><br>
- <shiro:hasAnyRoles name="admin">
- <a href="/jsp/admin.jsp">Admin Page</a>
- </shiro:hasAnyRoles>
- <br><br>
- <a href="../test/logout.do">Logout</a>
- </body>
- </html>
6.后台代码的编写
6.1 Controller层的编写
这次整合我么主要要实现登陆和登出功能,因此controller层我们这样写:
- package com.gray.user.controller;
- import java.io.IOException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.IncorrectCredentialsException;
- import org.apache.shiro.authc.UnknownAccountException;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.subject.Subject;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.RequestMapping;
- import com.gray.user.entity.User;
- import com.gray.user.service.impl.UserServiceImpl;
- @Controller
- @RequestMapping("/test")
- public class LoginController {
- @Autowired
- private UserServiceImpl userService;
- @RequestMapping("/dologin.do") //url
- public String dologin(User user, Model model){
- String info = loginUser(user);
- if (!"SUCC".equals(info)) {
- model.addAttribute("failMsg", "用户不存在或密码错误!");
- return "/jsp/fail";
- }else{
- model.addAttribute("successMsg", "登陆成功!");//返回到页面说夹带的参数
- model.addAttribute("name", user.getUsername());
- return "/jsp/success";//返回的页面
- }
- }
- @RequestMapping("/logout.do")
- public void logout(HttpServletRequest request,HttpServletResponse response) throws IOException{
- Subject subject = SecurityUtils.getSubject();
- if (subject != null) {
- try{
- subject.logout();
- }catch(Exception ex){
- }
- }
- response.sendRedirect("/index.jsp");
- }
- private String loginUser(User user) {
- if (isRelogin(user)) return "SUCC"; // 如果已经登陆,无需重新登录
- return shiroLogin(user); // 调用shiro的登陆验证
- }
- private String shiroLogin(User user) {
- // 组装token,包括客户公司名称、简称、客户编号、用户名称;密码
- UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword().toCharArray(), null);
- token.setRememberMe(true);
- // shiro登陆验证
- try {
- SecurityUtils.getSubject().login(token);
- } catch (UnknownAccountException ex) {
- return "用户不存在或者密码错误!";
- } catch (IncorrectCredentialsException ex) {
- return "用户不存在或者密码错误!";
- } catch (AuthenticationException ex) {
- return ex.getMessage(); // 自定义报错信息
- } catch (Exception ex) {
- ex.printStackTrace();
- return "内部错误,请重试!";
- }
- return "SUCC";
- }
- private boolean isRelogin(User user) {
- Subject us = SecurityUtils.getSubject();
- if (us.isAuthenticated()) {
- return true; // 参数未改变,无需重新登录,默认为已经登录成功
- }
- return false; // 需要重新登陆
- }
- }
6.2 实现自定义Realm
shiro从Realm获取安全数据,也就是说SecurityManager要验证身份,它需要从Realm获取相应的用户进行比较以确定用户的身份是否合法;我们可以把Realm看作是DataSource,安全数据源。实现自定义Realm主要是继承AuthrizingRealm这个父类,重写doGetAuthrizationInfo和doGetAuthenticationInfo这两个方法,其中doGetAuthenticationInfo是用来验证用户合法性的,根据输入的用户信息从数据库中查出用户,根据用户情况抛出不同的异常。doGetAuthrizationInfo是对当前用的用户进行授权的。在这里我把角色写死了。在实际项目开发中应该是在表中做权限和角色的定义再从数据库中查出一个集合然后迭代授权。具体实现代码如下:
- package com.gray.base.shiro;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.SimpleAuthenticationInfo;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.authz.SimpleAuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.springframework.beans.factory.annotation.Autowired;
- import com.gray.user.entity.User;
- import com.gray.user.service.impl.UserServiceImpl;
- public class ShiroDbRealm extends AuthorizingRealm {
- @Autowired
- private UserServiceImpl userService;
- public static final String SESSION_USER_KEY = "gray";
- /**
- * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
- User user = (User) SecurityUtils.getSubject().getSession().getAttribute(ShiroDbRealm.SESSION_USER_KEY);
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- info.addRole(user.getRole().trim());
- return info;
- }
- /**
- * 认证回调函数,登录信息和用户验证信息验证
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(
- AuthenticationToken authcToken) throws AuthenticationException {
- // 把token转换成User对象
- User userLogin = tokenToUser((UsernamePasswordToken) authcToken);
- // 验证用户是否可以登录
- User ui = userService.doUserLogin(userLogin);
- if(ui == null)
- return null; // 异常处理,找不到数据
- // 设置session
- Session session = SecurityUtils.getSubject().getSession();
- session.setAttribute(ShiroDbRealm.SESSION_USER_KEY, ui);
- //当前 Realm 的 name
- String realmName = this.getName();
- //登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
- // Object principal = ui.getUsername();
- Object principal = authcToken.getPrincipal();
- return new SimpleAuthenticationInfo(principal, userLogin.getPassword(), realmName);
- }
- private User tokenToUser(UsernamePasswordToken authcToken) {
- User user = new User();
- user.setUsername(authcToken.getUsername());
- user.setPassword(String.valueOf(authcToken.getPassword()));
- return user;
- }
- //一定要写getset方法
- public UserServiceImpl getUserService() {
- return userService;
- }
- public void setUserService(UserServiceImpl userService) {
- this.userService = userService;
- }
- }
在这里我们要注意的是用注解没办法注入我们要的service,一定要写getset方法,不然会报错(网上的解决办法挺有道理的但是试过没效果。。。。)。
具体的serviceImpl的实现我就不写出来了,数据库的查询我相信大家都会(默认你们都会)。随后如果有需要我会把整合代码放上来。
最后贴几张测试图:
登陆界面:
以管理员角色的账号登陆:
AdminPage:
以普通用户角色的账号登陆:
UserPage:
当你以普通用户登录却想访问adminpage时:
登陆失败:
最后登出点击logout返回登陆主界面。
以上就是整合Shiro的所有过程,当然在实际项目中不会那么简单但我觉得原理也是差不多的。学无止境,最后再次感谢网上提供资料的各位大神。
SpringMVC整合Shiro权限框架的更多相关文章
- SpringBoot整合Shiro权限框架实战
什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...
- SpringMVC整合Shiro安全框架(一)
一. 准备工作 1. 本文参考自张开涛的 <跟我学Shiro> 二. 简介 1. Apache Shiro是Java的一个安全框架.可以帮助我们完成:认证.授权.加密.会话管理.与Web集 ...
- Spring Boot:整合Shiro权限框架
综合概述 Shiro是Apache旗下的一个开源项目,它是一个非常易用的安全框架,提供了包括认证.授权.加密.会话管理等功能,与Spring Security一样属基于权限的安全框架,但是与Sprin ...
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
- SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能
SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...
- SpringMVC整合Shiro——(3)
SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能. 第一步:配置web.xml <!-- 配置Shiro过滤器,先让Shiro ...
- Shiro权限框架简介
http://blog.csdn.net/xiaoxian8023/article/details/17892041 Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论 ...
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制 以及跨域的问题也有涉及
- Spring整合Shiro 权限 角色 用户关系分析
Spring整合Shiro 权限 角色 用户关系分析 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 前置内容 之前我们学习了,使用注解的方式去完成权限的控制,当然,也是静态的,也就 ...
随机推荐
- 通过自动回复机器人学Mybatis 笔记:接口式编程
[接口式编程]尚未遇见Spring --> 代码量反而增加 1.增加约定,减少犯错的可能(不用直接去写字符串 修改点1:命名空间 修改点2:增加接口,方法名与配置文件中的id对应 package ...
- 3、debian8安装和处理
本博文仅作本人操作过程的记录,留作备忘.自强不息 QQ1222698 本文写于2016年1月10日09:35:45,首先向debian的创始人Ian Murdock表示沉痛惦念! http://bai ...
- Apache Avro# 1.8.2 Specification (Avro 1.8.2规范)二
h5 { text-indent: 0.71cm; margin-top: 0.49cm; margin-bottom: 0.51cm; direction: ltr; color: #000000; ...
- [APIO2010]特别行动队
题目描述 你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场.出于默契的考虑,同一支特别行动队中队员的编号 应该连续,即为形如(i, i + 1, ...
- python科学计算_numpy_函数库
1.常规函数与排序 常用统计函数: 求和:sum().均值:mean().标准差:std().方差:var().最小值:min().最大值:max().最大值与最小值之差:ptp().最大值的下标:a ...
- WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口
目录 WPF的消息机制(一)-让应用程序动起来 WPF的消息机制(二)-WPF内部的5个窗口 (1)隐藏消息窗口 (2)处理激活和关闭的消息的窗口和系统资源通知窗口 (3)用于用户交互的可见窗口 (4 ...
- dubbo源码—service export
在应用编写好服务并进行之后,dubbo负责将服务export出去,dubbo export服务的时候主要做了以下几件事: 将服务export到本地(根据scope的配置) 创建Invoker(启动本地 ...
- spring中使用Hibernate中的getCurrentSession报出:createQuery is not valid without active transaction
1.错误信息 HTTP Status 500 - createQuery is not valid without active transaction type Exception report m ...
- vue2.0笔记《二》组件
主要内容:如何注册组件.如何使用组件.父组件子组件之间值的传递 1.如何注册组件 第一步:通过import将子组件载入父组件的js中 // 第一步:通过import将子组件载入父组件的js中 impo ...
- ajax介绍及使用
一.什么是ajax:(只刷新局部页面的技术) AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发 ...