shiro权限管理(一)
本篇文章主要记录第一次接触shiro,权限细粒度到按钮级别,很多地方也生硬,欢迎圈错,指点,共同学习shiro。本篇主要以前后台结合为主采用(spring+mybatis+springMVC 融合了缓存memcached,可以先忽略,本人主要做后台springMVC也是刚刚了解,勉强凑合用,用的不好的地方,多多圈评,共同学习)后续会结合开涛的第二十章(无状态web集成应用)把权限控制在rest接口端。
一.简介
关于基本介绍网上很多,主要推荐开涛的《跟我学shiro》讲的非常详细(文末附上参考链接),本篇主要记录使用。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
二.项目配置
1.数据表的关系
5个表(用户表,角色表,权限表,用户角色关系表,角色权限关系表)
可能有的还分功能点表,功能点权限关系表等等(五个表出来之后 这个可以尝试)。
2.项目整体的一个结构图
3.配置spring和mybatis的一些基本信息,主要写在base.xml里面,主要是配置数据源,配置读取application.properties的信息
,集成mybatis,得到一个java类中dao层可用的jdbcTemplate,sqlSessionTemplate实例用来对数据库数据的增删改查操作。代码略……到这一步和shiro还是没有关系,可以test一下看是否能查询数据。
4.配置spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- 自动扫描controller包下的所有类,使其认为spring mvc的控制器 -->
<context:component-scan base-package="com.sss.controller" />
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" /><!-- json转换器 -->
</list>
</property>
</bean>
<!-- HandlerAdapter 表示所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean可以作为Spring
Web MVC中的处理器。如果需要其他类型的处理器可以通过实现HadlerAdapter来解决 -->
<bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/page/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
此处在web.xml中需要配上一段信息(附上整个web.xml)
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>sss</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 设置日志类型,可以直接加载logback ,配法很多--> <context-param> <param-name>resteasy.logger.type</param-name> <param-value>SLF4J</param-value> </context-param> <!-- 加载 applicationContext.xml--> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <!-- shiro 过滤器 --> <filter> <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>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <!-- spring 监听 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- springMvc配置 --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <description>spring mvc 配置文件</description> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <!-- 启动容器时初始化该Servlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
package com.sss.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ShowController {
@RequestMapping("show")
public ModelAndView showContent() {
// 1、收集参数、验证参数
// 2、绑定参数到命令对象
// 3、将命令对象传入业务对象进行业务处理
// 4、选择下一个页面
ModelAndView mv = new ModelAndView();
// 添加模型数据 可以是任意的POJO对象
mv.addObject("message", "框架测试!");
// 设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面
mv.setViewName("one");
return mv;
}
}
5.配置shiro
5.1在web.xml配置shiro的过滤器(详见4)
5.2 shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<description>Shiro 配置</description>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/page/login.jsp" />
<property name="filterChainDefinitions">
<value>
/ = authc
</value>
</property>
</bean>
<!-- 把realm 交给 securityManager 管理-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm -->
<property name="realm" ref="loginRealm" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!--自定义Realm 继承自AuthorizingRealm -->
<bean id="loginRealm" class="com.sss.realm.LoginRealmNew" />
<!-- 相当于调用SecurityUtils.setSecurityManager(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生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- 缓存管理器 -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
</beans>
/ = authc表示所有url都需要经过shiro的认证
<property name="loginUrl" value="/page/login.jsp" /> 登录页面不拦截
此处整个配置里面有个自定义realm。这个realm主要有两个方法,一个是认证,另一个就是授权。文末附上整个项目的代码需要的可以对着看。
三.流程与思路(代码只是片段,详见附件源码)
1.首先用户登录,从界面获取用户名和密码,且把密码加密,再通过用户名到数据库中查询该用户是否存在,如果存在,把登录的标志位置为true,不存在就直接return到登录界面且给提示。(详见LoginController)
1.1密码加密
package com.sss.comm; import org.apache.shiro.crypto.hash.Md5Hash; public class EncryptUtils { public static final String encryptMD5(String source) { if (source == null) { source = ""; } Md5Hash md5 = new Md5Hash(source); return md5.toString(); } }
1.2登录方法
@RequestMapping(params = "login")
public ModelAndView login(HttpServletRequest request) {
boolean isLogin = false;
String name = request.getParameter("name").toString();
String pwd = request.getParameter("pwd").toString();
ModelAndView mv = new ModelAndView();
// 密码加密
String encryptPwd = EncryptUtils.encryptMD5(pwd);
// 通过用户名查询密码
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", name);
List<Map<String, Object>> userList = userDao.selectAll(map);
if (userList.size() > 0) {
mv.addObject("name", name);
isLogin = true;
} else {
mv.addObject("msg", "用户名或密码不正确");
mv.setViewName("/login");
}
2.为真之后通过Subject currentUser = SecurityUtils.getSubject(); 获取当前用户,并由UsernamePasswordToken token = new UsernamePasswordToken(name,encryptPwd);把用户名和密码传递到shiro的过滤器,通过shiro提供的currentUser.login(token);跳转到自定义的realm进行认证。如果没有error说明认证成功,即登录成功,否者返回错误信息return到登录页面或同一的出错页面。
if (isLogin) {
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(name,
encryptPwd);
// token.setRememberMe(true);只记住当前会话id,可用于购物车功能,其余没什么用
try {
currentUser.login(token);
// 判断是否登录
if (currentUser.isAuthenticated()) {
mv.setViewName("index");
} else {
mv.addObject("msg", "login errors");
mv.setViewName("/login");
}
} catch (UnknownAccountException uae) {
mv.addObject("msg", "用户名或密码错误");
mv.setViewName("/login");
} catch (IncorrectCredentialsException ice) {
mv.addObject("msg", "用户名或密码错误");
mv.setViewName("/login");
} catch (LockedAccountException lae) {
mv.addObject("msg", "用户已经被锁定不能登录,请与管理员联系!");
mv.setViewName("/login");
} catch (ExcessiveAttemptsException eae) {
mv.addObject("msg", "错误次数过多!");
mv.setViewName("/login");
} catch (AuthenticationException ae) {
mv.addObject("msg", "其他的登录错误!");
mv.setViewName("/login");
}
3.登录之后进入到了主页(就好比你进入了一栋楼的大门,其中各个部门不知道是否有权限进去),此时触发导航栏或树形菜单操作,进入相应的controller
每触而发一次相当于调用一次接口就是一次url的请求。(详见ChenController)eg:
@RequestMapping(params = "main")
public ModelAndView main() {
ModelAndView mv = new ModelAndView();
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("chen.do?main")) {
mv.setViewName("chen");
} else {
mv.addObject("error", "无权访问该页面");
mv.setViewName("error");
}
return mv;
}
此时 通过
Subject currentUser = SecurityUtils.getSubject();得到当前用户
currentUser.isPermitted("chen.do?main")判断该用户是否有进入该地址的权限,此句相应realm中的授权方法。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection Principal) {
logger.info("授权参数--------------:{}", Principal);
String name = Principal.toString();
logger.info("realm 授权中的名字-----:{}", name);
AuthorizationInfo info = null;
if (name != null) {
info = allPermissions(name);
}
return info;
}
/***
* 根据用户名查出该用户的所有权限 绑定权限和角色
*
* @param name
* 传入的登录用户名
* @return
*/
private AuthorizationInfo allPermissions(String name) {
clearAllCachedAuthorizationInfo();
boolean isCache = true;
SimpleAuthorizationInfo info = null;
try {
info = (SimpleAuthorizationInfo) memcachedUtils.findCache(name
+ "cachePermission");
logger.info("缓存信息中的权限数据位:{}", info);
if (info != null) {
isCache = false;
}
} catch (Exception e1) {
logger.info("读取缓存失败:{}", e1.getMessage());
}
if (isCache) {
logger.info("------------------授权信息查询数据库");
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", name);
List<Map<String, Object>> allPermission = userDao
.selectPermissionByName(map);
if (allPermission.size() > 0) {
info = new SimpleAuthorizationInfo();
info.addRole(allPermission.get(0).get("role_name").toString());
Set<String> permissions = new HashSet<>();
for (Map<String, Object> m : allPermission) {
permissions.add(dom4jReadXML(m.get("permission_name")
.toString()));
}
info.setStringPermissions(permissions);
}
try {
memcachedUtils.addCache(name + "cachePermission", info, 1);
} catch (Exception e) {
logger.info("调用缓存存储出错:{}", e.getMessage());
e.printStackTrace();
}
}
return info;
}
如果有权限进入 则进入,否则提示信息 或者 return到统一的出错页面。由于每次触发授权事件都会查询数据库,所以我加到了缓存里面,如果有直接读取缓存信息,否则查询数据库,重新加入到缓存里面。
注:shiro的登录与授权 是两码事,并不是登录成功之后立马就拥有一个权限。登录和授权是分开的。触发授权方式很多,shiro开发手册上讲的很详细,还可以用注解等等方式……
按钮级别的权限和上诉一致处理。
4.由于我每次判断权限的时候都是要比对url,如果url改动,代码就会改动很大,所以引入配置文件(权限点与url的映射文件 permission-url-mapping.xml)
权限表存取的就是权限路劲,xml文件就与之对应,一但权限点变化,只需要改permission-url-mapping.xml即可。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 权限点与url的文件映射 -->
<root>
<!--横向菜单控制页面 -->
<user-miao>user.do?miao</user-miao>
<user-chen>user.do?chen</user-chen>
<user-quan>user.do?quan</user-quan>
<user-admin>user.do?admin</user-admin>
<!--chen控制页面 -->
<chen-main>chen.do?main</chen-main>
<chen-a>chen.do?a</chen-a>
<chen-b>chen.do?b</chen-b>
<!--miao控制页面 -->
<miao-main>miao.do?main</miao-main>
<miao-a>miao.do?a</miao-a>
<miao-b>miao.do?b</miao-b>
<!--quan控制页面 -->
<quan-main>quan.do?main</quan-main>
<quan-main-select>quan.do?main=click</quan-main-select>
<quan-a>quan.do?a</quan-a>
<quan-b>quan.do?b</quan-b>
<!--登录、登出 -->
<login-login>sss/login/login.do</login-login>
<login-loginout>login.do?loginout</login-loginout>
</root>
以xml文件存取,在判断权限的时候需解析该xml
/***
* 通过一个标签名获取xml中的值
*
* @param xmlTag
* @return
*/
private String dom4jReadXML(String xmlTag) {
logger.info("传入的节点名字:{}", xmlTag);
SAXReader reader = new SAXReader();
Document document = null;
// 与文件路径无关。有这个xml就能读取
String filePath = Thread.currentThread().getContextClassLoader()
.getResource("permission-url-mapping.xml").getPath();
File file = new File(filePath);
try {
// document = reader.read(new
// File("classpath:permission-url-mapping.xml"));
document = reader.read(file);
} catch (DocumentException e) {
logger.info("读取permission-url-mapping.xml文件失败原因", e.getMessage());
e.printStackTrace();
}
Element root = document.getRootElement();
String xmlVal = root.element(xmlTag).getTextTrim();
return xmlVal;
}
这样每次
currentUser.isPermitted("chen.do?main")的时候就可以写死为currentUser.isPermitted("chen-main") 里面即为permission-url-mapping.xml的标签名
5. 最后调用登出方法退出
/** * 退出登录 * * @return */ @RequestMapping(params = "loginout") public ModelAndView logout() { Subject currentUser = SecurityUtils.getSubject(); ModelAndView mv = new ModelAndView(); try { currentUser.logout(); mv.setViewName("login"); } catch (AuthenticationException e) { e.printStackTrace(); } return mv; }
总结:
1.本篇用到的shiro,只是很小的一部分,可能每个项目中用的都不一致。但是基本可以达到按钮级别的控制,效率未测试。
2.不足之处,shiro的很多功能都没用到,比如说shiro里面的缓存,与rest接口的风格集成,可以直接把权限配置在shiro.xml文件中,等等……
参考: http://jinnianshilongnian.iteye.com/blog/2018398
相关推荐
总的来说,“shiro权限管理案例加文档”会是一个全面的资源,涵盖了Shiro的各个方面,帮助开发者理解和应用Shiro进行权限管理,提升应用程序的安全性。通过深入学习和实践,我们可以更好地利用Shiro来满足项目的安全...
SSM+Shiro权限管理Demo是一个综合性的项目实例,它结合了Spring、Spring MVC(SSM)和Apache Shiro框架来实现一个完善的权限控制体系。这个项目不仅涉及到后端的权限设计,还涵盖了前端页面的展示,使得用户界面与...
SSM-Shiro权限管理是Java Web开发中一种常见的权限控制框架集成方案,它结合了Spring、SpringMVC、MyBatis以及Apache Shiro这四个组件,实现了一个完整的权限管理系统。在本文中,我们将深入探讨如何将这四个组件...
在“ssm-shiro权限管理(二)”中,我们将深入探讨如何在Shiro的基础上进一步优化登录流程、实现缓存管理和动态权限分配。Shiro是一个强大且易用的Java安全框架,提供了认证、授权、会话管理和加密等功能,为应用...
Apache Shiro是一个强大且易用的Java安全框架,它提供了认证、授权、加密和会话管理功能,可以非常方便地用于构建和强化应用程序的安全性。本示例将深入讲解Shiro在权限管理中的应用。 首先,我们要理解Shiro的核心...
总的来说,"springboot-shiro权限管理系统"是一个集成了SpringBoot、Shiro和Bootstrap的高效解决方案,它为开发者提供了便捷的身份认证、权限控制和日志管理功能,是构建企业级Web应用的理想选择。通过理解和掌握这...
总的来说,SpringBoot+Shiro权限管理系统脚手架提供了一个快速开发企业级权限管理系统的模板,帮助开发者省去了许多基础架构的工作,可以更专注于业务逻辑的实现,提高开发效率,同时保证系统的安全性。对于初学者和...
本项目"springboot-shiro-demo_mybatisplus_DEMO_shiro权限管理_"结合了这三个技术,旨在实现一个高效且安全的权限管理系统。下面将详细介绍这些知识点。 **Spring Boot** Spring Boot是基于Spring框架的轻量级开发...
在这个"Springboot+shiro权限管理"项目中,文件`731.springboot-shiro__zhangyd-c`可能包含了一个完整的示例代码,包括上述所有步骤的实现。通过研究这个项目的源码,可以更好地理解如何在Spring Boot 应用中有效地...
Spring Boot 提供了简化 Java 应用程序开发的框架,而 Shiro 是一个轻量级的安全框架,专注于身份验证、授权、会话管理和加密。下面将详细阐述它们在权限认证管理中的应用及其相关知识点。 一、Spring Boot 概述 ...
总结起来,"shiro权限管理实例"为我们提供了一个学习Shiro权限控制的实践平台。通过这个实例,我们可以了解如何设置身份验证、授权、会话管理和加密,这对于任何需要用户权限管理的Java应用都是非常有价值的。
在本文中,我们将深入探讨Shiro权限管理的相关知识点,以及如何利用它来实现高效的安全控制。 1. **Shiro基础概念** - **身份验证(Authentication)**:确认用户身份的过程,通常涉及用户名和密码的校验。 - **...
在这个"shiro权限管理类项目框架"中,我们将深入探讨 Shiro 如何帮助我们实现权限控制。 Shiro 的核心组件包括: 1. **认证**:这是验证用户身份的过程。在 Shiro 中,我们可以创建一个 `Subject` 对象代表当前...
### SpringMVC+Shiro权限管理系统详解 #### 权限管理概述 权限管理在软件开发中扮演着至关重要的角色,特别是在涉及多用户交互的应用场景下。通过合理的权限控制,可以确保不同用户仅能访问和操作他们被授权的功能...
移动互联网场景下服务端Shiro权限管理研究 1. 背景介绍 在移动互联网时代,Web系统、App和微信小程序等多种形式的混合应用层出不穷。随之而来的是权限管理的复杂性显著增加。当前的系统权限管理存在授权方式复杂、...
Shiro是一个开源的Java...在配置和使用Shiro进行权限管理时,需要对框架有一个全面的理解,包括上述提到的认证、授权、会话管理等核心概念,以及框架的架构细节,这样才能有效地利用Shiro构建出安全稳定的应用程序。
Shiro是一个广泛使用的Java安全框架,它为应用程序提供了认证、授权、会话管理和加密等功能。在Go语言环境中,我们可以采用类似的设计模式,构建出高效且灵活的权限管理系统。 首先,我们需要理解Shiro的核心概念:...
Apache Shiro是一个强大的Java安全框架,它提供了身份验证、授权(权限管理)、会话管理和加密服务。这个示例项目提供了一个完整的Shiro实现,特别强调了权限管理,并且结合了数据库来存储用户和角色信息。从提供的...
spring+mybatis+shiro整合。。。简单入门案例,maven项目