前言
这是shiro的初期学习笔记,如有错误,欢迎各位大佬指出错误!以下教程来源
在springBoot中使用shiro
创建项目这些步骤就不赘述了;项目创建好之后,在src下添加一个webapp目录,并且添加一个WEB-INF目录,形成如下项目结构:
然后在项目结构中将webapp作为项目资源,如下:
ps:
如果不自定义项目资源上面的步骤都可以省略。。。。
然后在application.properties中添加项目配置,如下:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis.mapper-locations=classpath:mapper/ *.xml
mybatis.type-aliases-package=com.drillsb.demo.pojo
添加依赖,如下:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.drillsb</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<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>
<!-- 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>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 资源路径-->
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
注意:添加的是shiro的依赖如下:
<!-- 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>
还有就是是自定义项目资源路径,所以在pom.xml中也必须要配置,如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 资源路径-->
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
之后就是项目的各个页面以及类,直接在
shiro学习基础(二)之web例子中的ssm中是一样的,copy过来就可以了,形成的项目结构如下:
需要改变的地方
替代applicationContext-shiro文件
替代在ssm中的applicationContext-shiro的配置文件作用,我们需要创建一个配置文件类,如下:
package com.drillsb.demo.shiro;
import com.drillsb.demo.filter.UrlPathMatchingFilter;
import com.drillsb.demo.realm.DatabaseRealm;
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 javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration//表示这是个配置文件类
public class ShiroConfiguration {
/*
* @Bean表示该方法的返回值归spring管理
* */
@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;
}
/*
* 需要注意一点,URLPathMatchingFilter 并没有用@Bean管理起来。
* 原因是Shiro的bug, 这个也是过滤器,ShiroFilterFactoryBean 也是过滤器,
* 当他们都出现的时候,默认的什么anno,authc,logout过滤器就失效了。所以不能把他声明为@Bean。
* */
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;
}
}
在上面配置的UrlPathMatchingFilter类,并没有在spring的管理中,也就是说当我们注入对象给予这个类的时候,也无法生效;
为了解决这个问题,需要手动将对象注入到这个类中;
所以需要在util包下创建一个类SpringContextUtils,该类继承了ApplicationContextAware,关于这个类引用博文Spring中的ApplicationContextAware接口的使用中提到了,继承了这个类能够轻松的获取所有的spring管理的bean,
SpringContextUtils如下:
package com.drillsb.demo.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/*
在ShiroConfiguration中配置URLPathMatchingFilter 没有被声明为@Bean
也就是无法在spring中管理这个拦截器类
同样也无法在该URLPathMatchingFilter类中注入PermissionService
* 在业务上URLPathMatchingFilter 里面又必须使用PermissionService类
这个类就是解决这个问题存在的
* */
@Component
public class SpringContextUtils implements ApplicationContextAware {//是你
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
// 把ApplicationContext上下文对象给我们创建的类
SpringContextUtils.context=applicationContext;
}
// 获取上下文对象
public static ApplicationContext getContext() {
return context;
}
}
然后在UrlPathMatchingFilter类中,就可以手动将要注入到该类的属性注入了,如下:
package com.drillsb.demo.filter;
import com.drillsb.demo.service.PermissionService;
import com.drillsb.demo.util.SpringContextUtils;
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 javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;
public class UrlPathMatchingFilter extends PathMatchingFilter {
@Autowired
PermissionService permissionService;
@Override
protected boolean onPreHandle(ServletRequest request,
ServletResponse response,
Object mappedValue) throws Exception {
// System.out.println("话说这个到底是有没有被spring管理: "+permissionService);
/*
* 这一步是必要的,不然就是null
* */
if(null==permissionService){
permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
}
// 获取访问的地址
String requestURL=getPathWithinApplication(request);
// System.out.println("这个东西是啥?: "+requestURL);
Subject subject = SecurityUtils.getSubject();
// 如果没有登录就跳转到登录页面
if(!subject.isAuthenticated()){
WebUtils.issueRedirect(request, response, "/login");
return false;
}
boolean needInterceptor = permissionService.needInterceptor(requestURL);
// 如果访问的路径在权限表中不存在,那么就放行
if(!needInterceptor){
return true;
}else{
boolean hasPermission=false;
// 获取用户名
String userName = subject.getPrincipal().toString();
// 通过用户名获取该用户拥有的权限url集合
Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
for (String userPermissionUrl : permissionUrls) {
// 如果该用户权限url集合中包含了访问的路径url
if(userPermissionUrl.equals(requestURL)){
hasPermission=true;
break;
}
}
if (hasPermission){
return true;
} else {
UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURL + " 的权限");
subject.getSession().setAttribute("ex", ex);
WebUtils.issueRedirect(request, response, "/unauthorized");
return false;
}
}
}
}
修改copy过来的xml文件
因为mapper类中的xml文件是直接copy在ssm项目中的,所以需要修改其xml中的类型,将所有的包名都改为自己项目所在的包名,不然就会报错
修改jsp文件的静态资源引入
分为两种,一种是不需要访问以及目录的引入,根据在pom.xml文件配置的,引入静态文件路径写法如下:
另一种是在请求路径中添加了一级目录的,如下引入写法:
其它的没啥变化,运行主程序文件,如下: