前提

写之前纠结了一番,这一节放在shiro里面还是springboot里面。后来想了下,还是放springboot里吧,因为这里没有shiro的新东西,只有springboot添加了新东西的使用。

但是这里还是基于shiro那边的代码已经内容来写的,对权限和角色做控制。

pom.xml

按照我个人习惯,新建一个boot工程,首先当然是先配置一下基本的pom.xml文件啦。后期再开发的时候,需要什么再加。

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.how2java</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro</name>
<description>shiro</description>
<packaging>war</packaging> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- servlet依赖. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- tomcat的支持.-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
</dependency> <!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency> <!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency> <!-- mybatis 逆向工程 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency> <!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency> <!-- shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies> <properties>
<java.version>1.8</java.version>
</properties> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

点击展开

application.properties

然后,配置application.properties文件或者application.yml文件,我是喜欢yml风格的。

server:
port: 8084
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
datasource:
url: jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver

ShiroConfiguration

springboot最大的特点是啥呀?没有配置文件呐,但是我们使用别人的工具,人家需要添加配置文件,怎么办?在我看来,这是springboot的一个缺点吧,它居然是写配置类,感觉又回到过去了。因为配置文件的方式可读性要强。而且配置文件的方式就是由配置类发展过来的。嗯嗯嗯~~~~

ShiroConfiguration 这个类就是shiro配置类,类名怎么命名都可以,就是要用@Configuration 注解表示它是一个配置类

@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}

这种写法就和 applicationContext-shiro.xml 中的

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

是同一个作用,只要用@Bean 注解了,就表示是被spring管理起来的对象了。

这个类里面就声明了SecurityManager,DatabaseRealm, HashedCredentialsMatcher,ShiroFilterFactoryBean 等等东西,只是写法变化了,其实和配置文件里是一样的。
需要注意一点,URLPathMatchingFilter 并没有用@Bean管理起来。 原因是Shiro的bug, 这个也是过滤器,ShiroFilterFactoryBean 也是过滤器,当他们都出现的时候,默认的什么anno,authc,logout过滤器就失效了。所以不能把他声明为@Bean。

public URLPathMatchingFilter getURLPathMatchingFilter() {
return new URLPathMatchingFilter();
}
 package com.how2java.shiro;

 import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import com.how2java.filter.URLPathMatchingFilter;
import com.how2java.realm.DatabaseRealm; @Configuration
public class ShiroConfiguration {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} /**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
Filter Chain定义说明
1、一个URL可以配置多个Filter,使用逗号分隔
2、当设置多个过滤器时,全部验证通过,才视为通过
3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
//自定义拦截器
Map<String, Filter> customisedFilter = new HashMap<>();
customisedFilter.put("url", getURLPathMatchingFilter()); //配置映射关系
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/index", "anon");
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/config/**", "anon");
filterChainDefinitionMap.put("/doLogout", "logout");;
filterChainDefinitionMap.put("/**", "url");
shiroFilterFactoryBean.setFilters(customisedFilter);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
} public URLPathMatchingFilter getURLPathMatchingFilter() {
return new URLPathMatchingFilter();
} @Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm.
securityManager.setRealm(getDatabaseRealm());
return securityManager;
} @Bean
public DatabaseRealm getDatabaseRealm(){
DatabaseRealm myShiroRealm = new DatabaseRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
} /**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码;
* )
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher;
} /**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}

点击展开

SpringContextUtils.java

继续上面的知识点,因为URLPathMatchingFilter 没有被声明为@Bean, 那么换句话说 URLPathMatchingFilter 就没有被Spring管理起来,那么也就无法在里面注入 PermissionService类了。 
但是在业务上URLPathMatchingFilter 里面又必须使用PermissionService类,怎么办呢? 就借助SpringContextUtils 这个工具类,来获取PermissionService的实例。 
这里提供工具类,下个步骤讲解如何使用这个工具类。

package com.how2java.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext context; public void setApplicationContext(ApplicationContext context)
throws BeansException {
SpringContextUtils.context = context;
} public static ApplicationContext getContext(){
return context;
}
}

URLPathMatchingFilter.java

因为前面两个步骤的原因,既不能把 URLPathMatchingFilter.java 作为@Bean管理起来,又需要在里面使用 PermissionService,所以就用上一步的工具类 SpringContextUtils.java,通过如下方式获取 PermissionService了:

permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);

 package com.how2java.filter;

 import java.util.Arrays;
import java.util.Set; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired; import com.how2java.service.PermissionService;
import com.how2java.service.impl.PermissionServiceImpl;
import com.how2java.util.SpringContextUtils; public class URLPathMatchingFilter extends PathMatchingFilter {
@Autowired
PermissionService permissionService; @Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
if(null==permissionService)
permissionService = SpringContextUtils.getContext().getBean(PermissionService.class); String requestURI = getPathWithinApplication(request);
System.out.println("requestURI:" + requestURI); Subject subject = SecurityUtils.getSubject();
// 如果没有登录,就跳转到登录页面
if (!subject.isAuthenticated()) {
WebUtils.issueRedirect(request, response, "/login");
return false;
} // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
System.out.println("permissionService:"+permissionService);
boolean needInterceptor = permissionService.needInterceptor(requestURI);
if (!needInterceptor) {
return true;
} else {
boolean hasPermission = false;
String userName = subject.getPrincipal().toString();
Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
for (String url : permissionUrls) {
// 这就表示当前用户有这个权限
if (url.equals(requestURI)) {
hasPermission = true;
break;
}
} if (hasPermission)
return true;
else {
UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限"); subject.getSession().setAttribute("ex", ex); WebUtils.issueRedirect(request, response, "/unauthorized");
return false;
} } }
}

点击展开

测试

启动该springboot工程,大家试下效果吧。


代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/springboot-shiro.zip

springboot(六)-使用shiro的更多相关文章

  1. SpringBoot整合Apache Shiro权限验证框架

    比较常见的权限框架有两种,一种是Spring Security,另一种是Apache Shiro,两种框架各有优劣,个人感觉Shiro更容易使用,更加灵活,也更符合RABC规则,而且是java官方更推 ...

  2. SpringBoot集成Apache Shiro

    笔者因为项目转型的原因,对Apache Shiro安全框架做了一点研究工作,故想写点东西以便将来查阅.之所以选择Shiro也是看了很多人的推荐,号称功能丰富强大,而且易于使用.实践下来的确如大多数人所 ...

  3. springboot 整合apache shiro

    这几天因为项目需要,学习了下shiro,由此留下一些记录,也希望对初学shiro的朋友有帮助. springboot 是这两年新兴起来的一个项目,它的出现是为了减少springmvc开发过程中需要引入 ...

  4. Shiro(三):Spring-boot如何集成Shiro(下)

    上一篇文章介绍了shiro在spring-boot中通过filter实现authentication流程(通过设置filterMaps也可以达到authorization的目的):这篇文章主要介绍sp ...

  5. Shiro(二):Spring-boot如何集成Shiro(上)

    这篇文章主要介绍了spring-boot是如何集成shiro的authentication流程的. 从shiro-spring-boot-web-starter说起 shiro-spring-boot ...

  6. SpringBoot:整合Shiro

    目录 1.Shiro简介 1.1.什么是Shiro? 1.2.有哪些功能 1.3.Shiro架构(外部) 1.4.Shiro架构(内部) 2.HelloWorld 3.Shiro整合Spring Bo ...

  7. springboot thymeleaf和shiro标签整合

    这里用的是 thymeleaf 2.x版本的 添加依赖 <dependency> <groupId>com.github.theborakompanioni</group ...

  8. spring-boot(六) 邮件服务

    学习文章来自:springboot(十):邮件服务 简单使用 1.pom包配置 pom包里面添加spring-boot-starter-mail包引用 <dependencies> < ...

  9. Shiro笔记(六)Shiro标签的使用

    Shiro标签的使用 引入标签库 <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"% ...

随机推荐

  1. 2-配置Andriod环境时的错误。。。Theme.AppCompat.Light

    编译或运行时可能会出现错误: Error:Error retrieving parent for item: No resource found that matches the given name ...

  2. 目录、目录项、文件名、inode、软硬链接的关系

    对于Unix系列的操作系统,大多都有v节点.但是对于linux来说,只有通用的i节点,却没有v节点. 下面来探讨一下,linux下的i节点. linux中,文件查找不是通过文件名称来查找的.实际上是通 ...

  3. 数字图像处理实验(12):PROJECT 05-03,Periodic Noise Reduction Using a Notch Filter 标签: 图像处理MATLAB 2017-0

    实验要求: Objective: To understand the principle of the notch filter and its periodic noise reducing abi ...

  4. 使用 insertBefore 和insertAfter,在指定位置追加与删除元素

    来自于<sencha touch 权威指南> ----------------------------------- 除 append 和 overwrite 外,还可以使用 insert ...

  5. tensorflow 中 feed的用法

    Feed 上述示例在计算图中引入了 tensor, 以常量或变量的形式存储. TensorFlow 还提供了 feed 机制, 该机制 可以临时替代图中的任意操作中的 tensor 可以对图中任何操作 ...

  6. Python3 网络爬虫开发实战学习弱点书签

    1. urllib.robotparse模块对robot.txt文件的解析,can_fetch()方法和parse()方法. Page121 2. lxml.etree模块自动补全Html代码,Htm ...

  7. Ajax——jQuery实现

    紧接上文 一.load()方法 load() 方法师jQuery中最为简单和常用的Ajax方法,能载入远程的HTML代码并插入到DOM中.它的机构是:load(url[,data][,callback ...

  8. Customizing Site-Wide Behavior for ASP.NET Web Pages (Razor) Sites

    Customizing Site-Wide Behavior for ASP.NET Web Pages (Razor) Sites By Tom FitzMacken|February 17, 20 ...

  9. Python Lambda 的简单用法

    下面代码简单举例介绍以下 lambda的用法. from functools import reduce #1 python lambda会创建一个函数对象,但不会把这个函数对象赋给一个标识符,而de ...

  10. sql server重建系统数据库

    方法一:https://bbs.csdn.net/topics/100013082 方法二:http://blog.51cto.com/jimshu/1095780 *** 方法三:https://b ...