Spring Boot 添加Shiro支持
前言:
Shiro是一个权限、会话管理的开源Java安全框架;Spring Boot集成Shiro后可以方便的使用Session;
工程概述:
(工程结构图)
一、建立Spring Boot工程
参照http://www.cnblogs.com/liangblog/p/5207855.html 建立一个SpringBoot工程;
二、修改pom.xml,引入所需jar包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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.ddd</groupId> <artifactId>programme</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>programme</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jdbc-core</artifactId> <version>1.2.1.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-cas</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.jasig.cas.client</groupId> <artifactId>cas-client-core</artifactId> <version>3.4.1</version> </dependency> </dependencies> <!-- Package as an executable jar --> <build> <defaultGoal>compile</defaultGoal> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <!-- Add Spring repositories --> <!-- (you don't need this if you are using a .RELEASE version) --> <!-- <repositories> <repository> <id>spring-snapshots</id> <url>http://repo.spring.io/snapshot</url> <snapshots><enabled>true</enabled></snapshots> </repository> <repository> <id>spring-milestones</id> <url>http://repo.spring.io/milestone</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <url>http://repo.spring.io/snapshot</url> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <url>http://repo.spring.io/milestone</url> </pluginRepository> </pluginRepositories> --> </project>
主要引入日志、数据库,shrio相关的jar包
三、添加配置文件和日志文件
配置文件需要放到resource文件夹下(指向classpath),并取名aplication.properties,工程会自动查找到文件配置信息
配置文件:
displayname = pm server.port=8011 server.session.timeout=1000 server.tomcat.uri-encoding=UTF-8 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 logging.config=classpath:pm-logback.xml logging.path=log
日志文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<configuration scan="true" scanPeriod="10 seconds"> <include resource="org/springframework/boot/logging/logback/base.xml" /> <!-- 日志的级别:debug,info,warn,error --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> --> <File>${LOG_PATH}/info.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/pm-info-%d{yyyyMMdd}.log.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>2</maxHistory> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n </Pattern> </layout> </appender> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <File>${LOG_PATH}/error.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_PATH}/pm-error-%d{yyyyMMdd}.log.%i </fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>2</maxHistory> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n </Pattern> </layout> </appender> <root level="INFO"> <appender-ref ref="INFO_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </configuration>
部署工程时可以放到工程jar所在文件夹内,部署的工程会自动找到文件;
四、添加Shiro支持功能:
1、新建shiro配置类,Shiro的主要配置信息都在此文件内实现;
package com.ddd.program.config; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ShiroConfiguration { /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //filterChainDefinitionMap.put("/page/*", "authc"); // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 //filterChainDefinitionMap.put("/security/logoff", "logout"); shiroFilterFactoryBean.setLoginUrl("/#/login"); // 未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); MyRealm myRealm = new MyRealm(); securityManager.setRealm(myRealm); securityManager.setSessionManager(sessionManager()); securityManager.setCacheManager(ehCacheManager()); return securityManager; } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); Collection<SessionListener> listeners = new ArrayList<SessionListener>(); listeners.add(new MySessionListener()); sessionManager.setSessionListeners(listeners); return sessionManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager ehCacheManager = new EhCacheManager(); //ehCacheManager.setCacheManagerConfigFile("classpath:encache.xml"); return ehCacheManager; } }
2、新建登录验证和Session监听的类(上面的类所需要的两个类)
登录相关:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.ddd.program.config; 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.UnknownAccountException; 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.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cdv.nsite.program.model.AppUser; import com.cdv.nsite.program.services.impl.AppUserServiceImpl; public class MyRealm extends AuthorizingRealm { private final static Logger logger = LoggerFactory.getLogger(MyRealm.class); @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { logger.debug("登录验证后进行权限认证...."); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { logger.debug("登录操作进行登录认证......"); UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; AppUser user = SpringContextUtils.getBean(AppUserServiceImpl.class).getUserByName(token.getUsername()); if (user == null) { // 没找到帐号 throw new UnknownAccountException( "没有在本系统中找到对应的用户信息。"); } //简单验证 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo( user.getUsername(),user.getPassword(),getName()); return info; } }
Session相关:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.ddd.program.config; import java.util.concurrent.atomic.AtomicInteger; import org.apache.shiro.session.Session; import org.apache.shiro.session.SessionListener; import org.springframework.stereotype.Component; @Component public class MySessionListener implements SessionListener { private final AtomicInteger sessionCount = new AtomicInteger(0); @Override public void onStart(Session session) { sessionCount.incrementAndGet(); System.out.println("登录+1=="+sessionCount.get()); } @Override public void onStop(Session session) { sessionCount.decrementAndGet(); System.out.println("登录退出-1=="+sessionCount.get()); } @Override public void onExpiration(Session session) { sessionCount.decrementAndGet(); System.out.println("登录过期-1=="+sessionCount.get()); } public int getSessionCount() { return sessionCount.get(); } }
3、添加静态页面:
在resource文件夹下新建static或public文件夹,工程会默认找static文件夹下的index.*,没有在找public文件夹下的index.*;
4、新建登录的接口和实现
完整代码---> https://github.com/liangguang/springboot_18
结述:
以上代码只是个例子,主要是获得登录的Session用作其他用途;
---------------------------------------------------添加热部署支持-----------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!-- 模板引擎 暂时不用 --> <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> --> <!-- devtools可以实现热部署。devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机),注意:因为其采用的虚拟机机制,该项重启是很快的 如果设置SpringApplication.setRegisterShutdownHook(false),则自动重启将不起作用。 设置 spring.devtools.restart.enabled 属性为false,可以关闭该特性。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- optional=true,依赖不会传递,--> </dependency>
spring.devtools.restart.enabled = true
application.properties中添加配置项;