项目结构图:
强力推荐《跟我学shiro》!!
一、先新建一个maven项目,配置pom.xml
-
<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/maven-v4_0_0.xsd”>
-
<modelVersion>4.0.0
</modelVersion>
-
<groupId>com.test
</groupId>
-
<artifactId>Test
</artifactId>
-
<packaging>war
</packaging>
-
<version>0.0.1
</version>
-
<name>Test
</name>
-
<url>http://maven.apache.org
</url>
-
<properties>
-
<maven.compiler.source>1.8
</maven.compiler.source>
-
<maven.compiler.target>1.8
</maven.compiler.target>
-
</properties>
-
<dependencies>
-
<dependency>
-
<groupId>junit
</groupId>
-
<artifactId>junit
</artifactId>
-
<version>3.8.1
</version>
-
<scope>test
</scope>
-
</dependency>
-
<!–1、 spring相关包 –>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-aop
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
<!– 要使用spring的aop,要么引入aspectj,要么cglib –>
-
<dependency>
-
<groupId>org.aspectj
</groupId>
-
<artifactId>aspectjweaver
</artifactId>
-
<version>1.6.11
</version>
-
<type>jar
</type>
-
<scope>compile
</scope>
-
</dependency>
-
<dependency>
-
<groupId>cglib
</groupId>
-
<artifactId>cglib
</artifactId>
-
<version>3.2.4
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-beans
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-webmvc
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-jdbc
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-web
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
<!– 事务相关spring包 –>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-tx
</artifactId>
-
<version>4.3.2.RELEASE
</version>
-
</dependency>
-
-
<!–2、 数据库相关包 –>
-
<!– 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 –>
-
<dependency>
-
<groupId>commons-dbcp
</groupId>
-
<artifactId>commons-dbcp
</artifactId>
-
<version>1.4
</version>
-
</dependency>
-
<!– 导入Mysql数据库链接jar包 –>
-
<dependency>
-
<groupId>mysql
</groupId>
-
<artifactId>mysql-connector-java
</artifactId>
-
<!– Maven中driverClassName为com.mysql.jdbc.Driver时不可使用6.0.3版本 –>
-
<version>5.1.18
</version>
-
</dependency>
-
<!– mybatis相关包 –>
-
<dependency>
-
<groupId>org.mybatis
</groupId>
-
<artifactId>mybatis
</artifactId>
-
<version>3.4.2
</version>
-
</dependency>
-
<!– mybatis/spring包 –>
-
<dependency>
-
<groupId>org.mybatis
</groupId>
-
<artifactId>mybatis-spring
</artifactId>
-
<version>1.3.0
</version>
-
</dependency>
-
<!– Ehcache实现,用于参考 –>
-
<dependency>
-
<groupId>org.mybatis
</groupId>
-
<artifactId>mybatis-ehcache
</artifactId>
-
<version>1.0.0
</version>
-
</dependency>
-
-
<!– 3、日志文件管理包 –>
-
<!– log start –>
-
<dependency>
-
<groupId>log4j
</groupId>
-
<artifactId>log4j
</artifactId>
-
<version>1.2.17
</version>
-
</dependency>
-
<!– 格式化对象,方便输出日志 –>
-
<dependency>
-
<groupId>com.alibaba
</groupId>
-
<artifactId>fastjson
</artifactId>
-
<version>1.1.41
</version>
-
</dependency>
-
<!– 简单日志门面 –>
-
<dependency>
-
<groupId>org.slf4j
</groupId>
-
<artifactId>slf4j-api
</artifactId>
-
<version>1.7.7
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.slf4j
</groupId>
-
<artifactId>slf4j-log4j12
</artifactId>
-
<version>1.7.7
</version>
-
</dependency>
-
<!– log end –>
-
-
<!–4、 使用jackson处理对象与JSON之间相互转换 –>
-
<dependency>
-
<groupId>org.codehaus.jackson
</groupId>
-
<artifactId>jackson-mapper-asl
</artifactId>
-
<version>1.9.13
</version>
-
</dependency>
-
<!– 5、上传组件包 –>
-
<!– 需要与commons-io配合使用 –>
-
<dependency>
-
<groupId>commons-fileupload
</groupId>
-
<artifactId>commons-fileupload
</artifactId>
-
<version>1.3.1
</version>
-
</dependency>
-
<!– 开发IO流功能的工具类库 –>
-
<dependency>
-
<groupId>commons-io
</groupId>
-
<artifactId>commons-io
</artifactId>
-
<version>2.4
</version>
-
</dependency>
-
<!–6、 用来处理常用的编码方法的工具类包 –>
-
<dependency>
-
<groupId>commons-codec
</groupId>
-
<artifactId>commons-codec
</artifactId>
-
<version>1.9
</version>
-
</dependency>
-
<!– 7、支持servlet的jar包(HttpServletRequest、HttpServletResponse) –>
-
<dependency>
-
<groupId>javax.servlet
</groupId>
-
<artifactId>javax.servlet-api
</artifactId>
-
<version>3.1.0
</version>
-
</dependency>
-
<!–8、shiro相关包 –>
-
<dependency>
-
<groupId>org.apache.shiro
</groupId>
-
<artifactId>shiro-core
</artifactId>
-
<version>1.3.2
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.apache.shiro
</groupId>
-
<artifactId>shiro-web
</artifactId>
-
<version>1.3.2
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.apache.shiro
</groupId>
-
<artifactId>shiro-ehcache
</artifactId>
-
<version>1.3.2
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.apache.shiro
</groupId>
-
<artifactId>shiro-spring
</artifactId>
-
<version>1.3.2
</version>
-
</dependency>
-
<!– shiro整合util方法需要 –>
-
<dependency>
-
<groupId>com.google.collections
</groupId>
-
<artifactId>google-collections
</artifactId>
-
<version>1.0
</version>
-
</dependency>
-
<!– https://mvnrepository.com/artifact/com.google.guava/guava –>
-
<dependency>
-
<groupId>com.google.guava
</groupId>
-
<artifactId>guava
</artifactId>
-
<version>19.0
</version>
-
</dependency>
-
<!– 9、lombok 包–>
-
<dependency>
-
<groupId>org.projectlombok
</groupId>
-
<artifactId>lombok
</artifactId>
-
<version>1.16.16
</version>
-
</dependency>
-
<!– 10、编译jsp –>
-
<dependency>
-
<groupId>javax.servlet.jsp
</groupId>
-
<artifactId>jsp-api
</artifactId>
-
<version>2.2
</version>
-
</dependency>
-
<!– 11、JSTL标签类 –>
-
<dependency>
-
<groupId>jstl
</groupId>
-
<artifactId>jstl
</artifactId>
-
<version>1.2
</version>
-
</dependency>
-
-
<!– https://mvnrepository.com/artifact/javax.mail/mail –>
-
<dependency>
-
<groupId>com.fasterxml.jackson.core
</groupId>
-
<artifactId>jackson-core
</artifactId>
-
<version>2.8.3
</version>
-
</dependency>
-
<!– https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-core-asl –>
-
<dependency>
-
<groupId>org.codehaus.jackson
</groupId>
-
<artifactId>jackson-core-asl
</artifactId>
-
<version>1.9.13
</version>
-
</dependency>
-
<!– https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-mapper-asl –>
-
<dependency>
-
<groupId>com.fasterxml.jackson.core
</groupId>
-
<artifactId>jackson-databind
</artifactId>
-
<version>2.8.3
</version>
-
</dependency>
-
<!– https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations –>
-
<dependency>
-
<groupId>com.fasterxml.jackson.core
</groupId>
-
<artifactId>jackson-annotations
</artifactId>
-
<version>2.8.3
</version>
-
</dependency>
-
-
<dependency>
-
<groupId>org.apache.shiro
</groupId>
-
<artifactId>shiro-quartz
</artifactId>
-
<version>1.2.2
</version>
-
</dependency>
-
<!– https://mvnrepository.com/artifact/org.springframework/spring-context-support –>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-context-support
</artifactId>
-
<version>3.2.8.RELEASE
</version>
-
</dependency>
-
<dependency>
-
<groupId>net.sf.ehcache
</groupId>
-
<artifactId>ehcache-core
</artifactId>
-
<version>2.6.11
</version>
-
</dependency>
-
<dependency>
-
<groupId>commons-collections
</groupId>
-
<artifactId>commons-collections
</artifactId>
-
<version>3.2.1
</version>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework
</groupId>
-
<artifactId>spring-test
</artifactId>
-
<version> 3.2.4.RELEASE
</version>
-
<scope>provided
</scope>
-
</dependency>
-
</dependencies>
-
<build>
-
<finalName>Test
</finalName>
-
<pluginManagement>
-
<plugins>
-
<!– 逆向工程生成po类以及mapper –>
-
<plugin>
-
<groupId>org.mybatis.generator
</groupId>
-
<artifactId>mybatis-generator-maven-plugin
</artifactId>
-
<version>1.3.2
</version>
-
<configuration>
-
<configurationFile>src/main/resources/generator.xml
</configurationFile>
-
<verbose>true
</verbose>
-
<overwrite>true
</overwrite>
-
</configuration>
-
<executions>
-
<execution>
-
<id>Generate MyBatis Artifacts
</id>
-
<goals>
-
<goal>generate
</goal>
-
</goals>
-
</execution>
-
</executions>
-
<dependencies>
-
<dependency>
-
<groupId>org.mybatis.generator
</groupId>
-
<artifactId>mybatis-generator-core
</artifactId>
-
<version>1.3.2
</version>
-
</dependency>
-
</dependencies>
-
</plugin>
-
<!– 运用maven指令进行test –>
-
<plugin>
-
<groupId>org.apache.maven.plugins
</groupId>
-
<artifactId>maven-surefire-plugin
</artifactId>
-
<version>2.19.1
</version>
-
<configuration>
-
<skipTests>true
</skipTests>
-
</configuration>
-
</plugin>
-
<!– 默认加载本项目下的resource –>
-
<plugin>
-
<groupId>org.apache.maven.plugins
</groupId>
-
<artifactId>maven-resources-plugin
</artifactId>
-
<version>3.0.1
</version>
-
<configuration>
-
<encoding>UTF-8
</encoding>
-
</configuration>
-
</plugin>
-
<!– 运行maven指令运行tomcat –>
-
<plugin>
-
<groupId>org.apache.tomcat.maven
</groupId>
-
<artifactId>tomcat7-maven-plugin
</artifactId>
-
<version>2.2
</version>
-
</plugin>
-
</plugins>
-
</pluginManagement>
-
<!– 扫描的resource目录 –>
-
<resources>
-
<resource>
-
<directory>src/main/resources
</directory>
-
<includes>
-
<include>**/*
</include>
-
</includes>
-
<filtering>true
</filtering>
-
</resource>
-
</resources>
-
</build>
-
<!– 根据不同环境配置 –>
-
<profiles>
-
<profile>
-
<id>dev
</id>
-
<activation>
-
<activeByDefault>true
</activeByDefault>
-
</activation>
-
<properties>
-
<!– 数据库 –>
-
<ipPort>192.168.*.*:3306/dev_test
</ipPort>
-
</properties>
-
<build>
-
<!– 过滤的配置文件 –>
-
<filters>
-
<filter>src/main/resources/jdbc.properties
</filter>
-
<filter>src/main/resources/log4j.properties
</filter>
-
</filters>
-
</build>
-
</profile>
-
-
<profile>
-
<id>test
</id>
-
<properties>
-
<!– 数据库 –>
-
<ipPort>192.168.*.*:3306/creditloan_ph
</ipPort>
-
</properties>
-
<build>
-
<filters>
-
<filter>src/main/resources/jdbc.properties
</filter>
-
<filter>src/main/resources/log4j.properties
</filter>
-
</filters>
-
</build>
-
</profile>
-
<profile>
-
<id>product
</id>
-
<properties>
-
<!– 数据库 –>
-
<ipPort>192.168.*.*:3306/creditloan_ph
</ipPort>
-
</properties>
-
<build>
-
<filters>
-
<filter>src/main/resources/jdbc.properties
</filter>
-
<filter>src/main/resources/log4j.properties
</filter>
-
</filters>
-
</build>
-
</profile>
-
-
</profiles>
-
</project>
附:generator.xml
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<!DOCTYPE generatorConfiguration PUBLIC “-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN” “http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd”>
-
<generatorConfiguration>
-
<!– 数据库驱动包位置 –>
-
<classPathEntry
-
location=
“E:\apache-maven-3.3.9\repo\mysql\mysql-connector-java\5.1.18\mysql-connector-java-5.1.18.jar” />
-
<context id=“Tables” targetRuntime=“MyBatis3”>
-
<commentGenerator>
-
<!– 是否去除自动生成的注释 true:是 : false:否 –>
-
<property name=“suppressAllComments” value=“true” />
-
</commentGenerator>
-
<!– 数据库链接URL、用户名、密码 –>
-
<jdbcConnection driverClass=“com.mysql.jdbc.Driver”
-
connectionURL=
“jdbc:mysql://192.168.*.*:3306/dev_test”
userId=
“用户名”
-
password=
“密码”>
-
<!–<jdbcConnection driverClass=”oracle.jdbc.driver.OracleDriver” connectionURL=”jdbc:oracle:thin:@localhost:1521:orcl”
-
userId=”msa” password=”msa”> –>
-
</jdbcConnection>
-
<javaTypeResolver>
-
<!– 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer, 为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal –>
-
<property name=“forceBigDecimals” value=“true” />
-
</javaTypeResolver>
-
<!– 生成实体类的包名和位置,这里配置将生成的实体类放在com.loan.entity这个包下 –>
-
<javaModelGenerator targetPackage=“com.loan.entity”
-
targetProject=
“.\src\main\java\”>
-
<!– enableSubPackages:是否让schema作为包的后缀 –>
-
<property name=“enableSubPackages” value=“true” />
-
<!– 从数据库返回的值被清理前后的空格 –>
-
<property name=“trimStrings” value=“true” />
-
</javaModelGenerator>
-
<!– 生成的SQL映射文件包名和位置,这里配置将生成的SQL映射文件放在com.loan.dao.xml这个包下 –>
-
<sqlMapGenerator targetPackage=“com.loan.dao.xml”
-
targetProject=
“.\src\main\java\”>
-
<!– enableSubPackages:是否让schema作为包的后缀 –>
-
<property name=“enableSubPackages” value=“true” />
-
</sqlMapGenerator>
-
<!– 生成DAO的包名和位置,这里配置将生成的dao类放在com.loan.dao.mapper这个包下 –>
-
<javaClientGenerator type=“XMLMAPPER”
-
targetPackage=
“com.loan.dao.mapper”
targetProject=
“.\src\main\java\”>
-
<!– enableSubPackages:是否让schema作为包的后缀 –>
-
<property name=“enableSubPackages” value=“true” />
-
</javaClientGenerator>
-
<!– 要生成那些表(更改tableName和domainObjectName就可以) –>
-
<table tableName=“sys_user_role” domainObjectName=“UserRole”
-
enableCountByExample=
“false”
enableUpdateByExample=
“false”
-
enableDeleteByExample=
“false”
enableSelectByExample=
“false”
-
selectByExampleQueryId=
“false” />
-
</context>
-
</generatorConfiguration>
二、先把ssm的基本配置贴上来,就不详细赘述了:
1、application-context.xml(spring管理的相关配置)
-
<?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:context=
"http://www.springframework.org/schema/context"
-
xmlns:p=
"http://www.springframework.org/schema/p"
xmlns:aop=
"http://www.springframework.org/schema/aop"
-
xmlns:tx=
"http://www.springframework.org/schema/tx"
xmlns:mvc=
"http://www.springframework.org/schema/mvc"
-
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-
http://www.springframework.org/schema/mvc
-
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
-
http://www.springframework.org/schema/tx
-
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
-
http://www.springframework.org/schema/aop
-
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
-
http://www.springframework.org/schema/context
-
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
-
<!-- 第一步:【1.整合dao】 将Mybatis和Spring进行整合MyBatis和Spring整合,通过Spring管理mapper接口。使用mapper的扫描器自动扫描mapper接口在Spring中进行注册。 -->
-
<!-- 需要配置:a、数据源 b、SqlSessionFactory c、mapper扫描器 -->
-
<!-- 1、数据源定义 -->
-
<!-- (1)加载jdbc.properties、redis.properties文件中的内容 -->
-
<bean id="propertyConfigurer"
-
class=
"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
-
<property name="locations">
-
<list>
-
<value>classpath:jdbc.properties
</value>
-
<!-- <value>classpath:redis.properties</value> -->
-
</list>
-
</property>
-
</bean>
-
<!-- (2)mysql数据源配置 -->
-
<!-- a、数据源 -->
-
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
-
destroy-method=
"close">
-
<property name="driverClassName" value="${connection.driverClassName}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="36"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.url}" />
-
<property name="username" value="${connection.username}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="38"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.password}" />
-
<property name="maxActive" value="${connection.maxActive}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="40"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"maxIdle"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.maxIdle}" />
-
<property name="minIdle" value="${connection.minIdle}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="42"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"removeAbandoned"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.removeAbandoned}" />
-
<property name="removeAbandonedTimeout" value="${connection.removeAbandonedTimeout}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="44"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"logAbandoned"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.logAbandoned}" />
-
<property name="defaultAutoCommit" value="${connection.defaultAutoCommit}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="46"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"defaultReadOnly"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.defaultReadOnly}" />
-
<property name="validationQuery" value="${connection.validationQuery}"</span> /></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="48"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"testOnBorrow"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${connection.testOnBorrow}" />
-
</bean>
-
<!-- b、sqlSessionFactory:创建sqlSessionFactory,同时指定数据源 -->
-
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
-
<property name="dataSource" ref="dataSource">
</property>
-
<property name="configLocation" value="classpath:mybatis-config.xml" />
-
<!-- 自动扫描mapper目录, 省掉mybatis-config.xml里的手工配置 -->
-
<property name="mapperLocations">
-
<list>
-
<value>classpath:com/loan/dao/xml/*.xml
</value>
-
</list>
-
</property>
-
</bean>
-
<!-- c、mapper扫描器:通过扫描的模式,扫描目录在com/loan/mapper目录下 -->
-
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
-
<property name="basePackage" value="com.loan.dao.mapper" />
-
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
-
</bean>
-
<!-- 第二步:通过Spring管理Service接口。使用配置方式将Service接口配置在Spring配置文件中。实现事务控制。 -->
-
<!-- (事务管理) -->
-
<bean id="transactionManager"
-
class=
"org.springframework.jdbc.datasource.DataSourceTransactionManager">
-
<property name="dataSource" ref="dataSource">
</property>
-
</bean>
-
<!-- 使用annotation定义数据库事务,这样可以在类或方法中直接使用@Transactional注解来声明事务 -->
-
<tx:annotation-driven transaction-manager="transactionManager" />
-
<tx:advice id="txAdvice" transaction-manager="transactionManager">
-
<tx:attributes>
-
<tx:method name="save*" propagation="REQUIRED" />
-
<tx:method name="update*" propagation="REQUIRED" />
-
<tx:method name="delete*" propagation="REQUIRED" />
-
<tx:method name="load*" propagation="SUPPORTS" read-only="true" />
-
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
-
<tx:method name="search*" propagation="SUPPORTS" read-only="true" />
-
<tx:method name="approve" propagation="REQUIRED" />
-
<tx:method name="undo" propagation="REQUIRED" />
-
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
-
</tx:attributes>
-
</tx:advice>
-
<aop:config>
-
<aop:pointcut id="serviceMethod"
-
expression=
"execution(* com.loan.service..*.*(..))" />
-
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
-
</aop:config>
-
<!-- spring管理:自动搜索注解路径 在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的Java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean-->
-
<context:component-scan base-package="com.loan">
</context:component-scan>
-
</beans>
附:
a、jdbc.properties
connection.driverClassName=com.mysql.jdbc.Driver connection.url=jdbc:mysql://192.168.*.*:3306/dev_test?useUnicode=true&characterEncoding=UTF-8 connection.username=user connection.password=pwd connection.initialSize=0 connection.maxActive=100 connection.maxIdle=30 connection.minIdle=5 connection.maxWait=5000 connection.removeAbandoned=true connection.removeAbandonedTimeout=3000 connection.logAbandoned=false connection.defaultAutoCommit=true connection.defaultReadOnly=false connection.validationQuery=SELECT 1 connection.testOnBorrow=true
b、mybatis-config.xml
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config 3.0//EN” “http://mybatis.org/dtd/mybatis-3-config.dtd”>
-
<configuration>
-
<!–整合Spring的时候 只有 settings typeAliases mapper 三个属性有用, 其余的要在spring总配置文件中会覆盖 –>
-
<settings>
-
<!– 全局映射器,是否启用缓存 –>
-
<setting name=“cacheEnabled” value=“true” />
-
<!– 查询时,关闭关联对象即时加载以提高性能 –>
-
<!– 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能 –>
-
<setting name=“aggressiveLazyLoading” value=“false” />
-
<!– 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果 –>
-
<setting name=“multipleResultSetsEnabled” value=“true” />
-
<!– 允许使用列标签代替列名 –>
-
<setting name=“useColumnLabel” value=“true” />
-
<!– 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值),数据表的PK生成策略将被覆盖 –>
-
<setting name=“useGeneratedKeys” value=“true” />
-
<!– 给予被嵌套的resultMap以字段-属性的映射支持 –>
-
<setting name=“autoMappingBehavior” value=“FULL” />
-
<!– 对于批量更新操作缓存SQL以提高性能 –>
-
<!– <setting name=”defaultExecutorType” value=”BATCH” /> –>
-
<!– 数据库超过25000秒仍未响应则超时 –>
-
<setting name=“defaultStatementTimeout” value=“25000” />
-
</settings>
-
</configuration>
2、spring-mvc.xml(SpringMVC的相关配置)
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<beans xmlns=“http://www.springframework.org/schema/beans”
-
xmlns:aop=
“http://www.springframework.org/schema/aop”
xmlns:context=
“http://www.springframework.org/schema/context”
-
xmlns:mvc=
“http://www.springframework.org/schema/mvc”
xmlns:tx=
“http://www.springframework.org/schema/tx”
-
xmlns:xsi=
“http://www.w3.org/2001/XMLSchema-instance”
-
xsi:schemaLocation=
”http://www.springframework.org/schema/util
-
http://www.springframework.org/schema/util/spring-util-3.2.xsd
-
http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
-
http://www.springframework.org/schema/context
-
http://www.springframework.org/schema/context/spring-context-3.2.xsd
-
http://www.springframework.org/schema/tx
-
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
-
http://www.springframework.org/schema/jee
-
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
-
http://www.springframework.org/schema/mvc
-
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
-
http://www.springframework.org/schema/aop
-
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd”>
-
<!– 使用 mvc:annotation-driven代替注解映射器和注解适配器配置 mvc:annotation-driven默认加载很多的参数绑定方法,
-
比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
-
实际开发时使用mvc:annotation-driven –>
-
<mvc:annotation-driven />
-
<context:component-scan base-package=“com.loan” />
-
<bean
-
class=
“org.springframework.web.servlet.view.InternalResourceViewResolver”>
-
<property name=“prefix” value=“/WEB-INF/view/” />
-
<property name=“suffix” value=“.jsp” />
-
</bean>
-
</beans>
4、log4j.properties
log4j.rootLogger=INFO, stdout, logfile log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.threshold=INFO log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d \u4FE1\u9500\u7F51\u7AD9\u540E\u53F0\u7BA1\u7406 –>%5p{%F:%L}-%m%n log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender log4j.appender.logfile.threshold=ERROR log4j.appender.logfile.File=${catalina.home}/logs/Test/Test log4j.appender.logfile.DatePattern=’-‘yyyy-MM-dd-HH-mm’.log’ log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d \u4FE1\u9500\u7F51\u7AD9\ –>%5p{%F:%L}-%m%n
5、web.xml
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<web-app xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
-
xmlns=
“http://java.sun.com/xml/ns/javaee”
-
xsi:schemaLocation=
“http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd”
-
version=
“3.0”>
-
<context-param>
-
<param-name>webAppRootKey
</param-name>
-
<param-value>test
</param-value>
-
</context-param>
-
<context-param>
-
<param-name>log4jConfigLocation
</param-name>
-
<param-value>classpath:log4j.properties
</param-value>
-
</context-param>
-
<listener>
-
<listener-class>org.springframework.web.util.Log4jConfigListener
</listener-class>
-
</listener>
-
<filter>
-
<filter-name>CharacterEncodingFilter
</filter-name>
-
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
-
<init-param>
-
<param-name>encoding
</param-name>
-
<param-value>UTF-8
</param-value>
-
</init-param>
-
<init-param>
-
<param-name>forceEncoding
</param-name>
-
<param-value>true
</param-value>
-
</init-param>
-
</filter>
-
<filter-mapping>
-
<filter-name>CharacterEncodingFilter
</filter-name>
-
<url-pattern>/*
</url-pattern>
-
</filter-mapping>
-
<context-param>
-
<param-name>contextConfigLocation
</param-name>
-
<param-value>
-
classpath:application-context.xml
-
classpath:spring-shiro.xml
-
</param-value>
-
</context-param>
-
<listener>
-
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
-
</listener>
-
<servlet>
-
<servlet-name>dispatcher
</servlet-name>
-
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
-
<init-param>
-
<param-name>contextConfigLocation
</param-name>
-
<param-value>classpath:spring-mvc.xml
</param-value>
-
</init-param>
-
<load-on-startup>1
</load-on-startup>
-
</servlet>
-
<servlet-mapping>
-
<servlet-name>dispatcher
</servlet-name>
-
<url-pattern>/
</url-pattern>
-
</servlet-mapping>
-
<session-config>
-
<session-timeout>30
</session-timeout>
-
</session-config>
-
-
</web-app>
至此,ssm基本配置完成
三、整合shiro
1、spring-shiro.xml
-
<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”
-
xmlns:util=
“http://www.springframework.org/schema/util”
xmlns:jee=
“http://www.springframework.org/schema/jee”
-
xmlns:tx=
“http://www.springframework.org/schema/tx”
xmlns:mvc=
“http://www.springframework.org/schema/mvc”
-
xmlns:aop=
“http://www.springframework.org/schema/aop”
-
xsi:schemaLocation=
”
-
http://www.springframework.org/schema/util
-
http://www.springframework.org/schema/util/spring-util-3.0.xsd
-
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/tx
-
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
-
http://www.springframework.org/schema/jee
-
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
-
http://www.springframework.org/schema/mvc
-
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
-
http://www.springframework.org/schema/aop
-
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd”
-
default-lazy-init=
“false”>
-
<!– 缓存管理器使用Ehcache实现 –>
-
<bean id=“cacheManager” class=“org.apache.shiro.cache.ehcache.EhCacheManager”>
-
<property name=“cacheManagerConfigFile” value=“classpath:ehcache.xml” />
-
</bean>
-
<!– 凭证匹配器 –>
-
<bean id=“credentialsMatcher” class=“com.loan.credentials.RetryLimitHashedCredentialsMatcher”>
-
<constructor-arg ref=“cacheManager” />
-
<property name=“hashAlgorithmName” value=“md5” />
-
<property name=“hashIterations” value=“2” />
-
<property name=“storedCredentialsHexEncoded” value=“true” />
-
</bean>
-
<!– Realm实现 –>
-
<bean id=“userRealm” class=“com.loan.realm.UserRealm”>
-
<!– <property name=”userService” ref=”userService” /> –>
-
<property name=“credentialsMatcher” ref=“credentialsMatcher” />
-
<property name=“cachingEnabled” value=“true” />
-
<property name=“authenticationCachingEnabled” value=“true” />
-
<property name=“authenticationCacheName” value=“authenticationCache” />
-
<property name=“authorizationCachingEnabled” value=“true” />
-
<property name=“authorizationCacheName” value=“authorizationCache” />
-
</bean>
-
<!– 会话ID 生成器 –>
-
<bean id=“sessionIdGenerator”
-
class=
“org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator” />
-
<!– 会话DAO –>
-
<bean id=“sessionDAO”
-
class=
“org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO”>
-
<property name=“activeSessionsCacheName” value=“shiro-activeSessionCache” />
-
<property name=“sessionIdGenerator” ref=“sessionIdGenerator” />
-
</bean>
-
<!– 会话验证调度器 sessionValidationInterval:设置调度时间间隔 –>
-
<bean id=“sessionValidationScheduler”
-
class=
“org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler”>
-
<property name=“sessionValidationInterval” value=“18000000” />
-
<property name=“sessionManager” ref=“sessionManager” />
-
</bean>
-
<!– 会话Cookie 模板 –>
-
<bean id=“sessionIdCookie” class=“org.apache.shiro.web.servlet.SimpleCookie”>
-
<constructor-arg value=“sid” />
-
<property name=“httpOnly” value=“true” />
-
<property name=“maxAge” value=“1800” />
-
</bean>
-
<!– 记住密码cookie –>
-
<bean id=“rememberMeCookie” class=“org.apache.shiro.web.servlet.SimpleCookie”>
-
<constructor-arg value=“rememberMe” />
-
<property name=“httpOnly” value=“true” />
-
<property name=“maxAge” value=“2592000” />
<!– 30天 30*24*60*60 –>
-
</bean>
-
<!– rememberMe管理器,cipherKey是加密rememberMe Cookie的密钥;默认AES算 –>
-
<bean id=“rememberMeManager” class=“org.apache.shiro.web.mgt.CookieRememberMeManager”>
-
<property name=“cipherKey”
-
value=
“#{T(org.apache.shiro.codec.Base64).decode(‘4AvVhmFLUs0KTA3Kprsdag==’)}” />
-
<property name=“cookie” ref=“rememberMeCookie” />
-
</bean>
-
<!– 会话管理器 globalSessionTimeout:设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期 sessionValidationSchedulerEnabled:是否开启会话验证器,默认是开启的 –>
-
<bean id=“sessionManager”
-
class=
“org.apache.shiro.web.session.mgt.DefaultWebSessionManager”>
-
<property name=“globalSessionTimeout” value=“18000000” />
-
<property name=“deleteInvalidSessions” value=“true” />
-
<property name=“sessionValidationSchedulerEnabled” value=“true” />
-
<property name=“sessionValidationScheduler” ref=“sessionValidationScheduler” />
-
<property name=“sessionDAO” ref=“sessionDAO” />
-
<property name=“sessionIdCookieEnabled” value=“true” />
-
<property name=“sessionIdCookie” ref=“sessionIdCookie” />
-
<property name=“sessionListeners” ref=“sessionListener1” />
-
</bean>
-
<!– 监听会话 –>
-
<bean id=“sessionListener1” class=“com.loan.util.MySessionListener1”>
</bean>
-
<!– 安全管理器 –>
-
<bean id=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
<property name=“realm” ref=“userRealm” />
-
<property name=“sessionManager” ref=“sessionManager” />
-
<property name=“cacheManager” ref=“cacheManager” />
-
<property name=“rememberMeManager” ref=“rememberMeManager” />
-
</bean>
-
<!– kickoutSessionControlFilter 用于控制并发登录人数的 –>
-
<bean id=“kickoutSessionControlFilter”
-
class=
“com.loan.util.KickoutSessionControlFilter”>
-
<property name=“cacheManager” ref=“cacheManager” />
-
<property name=“sessionManager” ref=“sessionManager” />
-
<property name=“kickoutAfter” value=“false” />
-
<property name=“maxSession” value=“2” />
-
<property name=“kickoutUrl” value=“/login?kickout=1” />
-
</bean>
-
<!– 相当于调用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=“formAuthenticationFilter”
-
class=
“org.apache.shiro.web.filter.authc.FormAuthenticationFilter”>
-
<property name=“usernameParam” value=“username” />
-
<property name=“passwordParam” value=“password” />
-
<property name=“rememberMeParam” value=“rememberMe” />
-
<property name=“loginUrl” value=“/login” />
-
</bean>
-
<!– Shiro 的Web过滤器 –>
-
<bean id=“shiroFilter” class=“org.apache.shiro.spring.web.ShiroFilterFactoryBean”>
-
<property name=“securityManager” ref=“securityManager” />
-
<property name=“loginUrl” value=“/login” />
-
<property name=“successUrl” value=“/index” />
-
<property name=“unauthorizedUrl” value=“/unauthorized” />
-
<property name=“filters”>
-
<util:map>
-
<entry key=“authc” value-ref=“formAuthenticationFilter” />
-
<entry key=“kickout” value-ref=“kickoutSessionControlFilter”/>
-
</util:map>
-
</property>
-
<property name=“filterChainDefinitions”>
-
<value>
-
/static/** = anon
-
/index = anon
-
/unauthorized = anon
-
/login =authc
-
/logout = logout
-
/admin/**=user,kickout
-
</value>
-
</property>
-
</bean>
-
<bean
-
class=
“org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”>
-
<property name=“exceptionMappings”>
-
<props>
-
<prop key=“org.apache.shiro.authz.UnauthorizedException”>
-
/unauthorized
-
</prop>
-
<prop key=“org.apache.shiro.authz.UnauthenticatedException”>
-
/login
-
</prop>
-
</props>
-
</property>
-
</bean>
-
</beans>
附:
a、ehcache.xml
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<ehcache name=“es”>
-
-
<diskStore path=“java.io.tmpdir”/>
-
-
<!– 密码输入错误 锁定1小时 –>
-
<!– timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒) –>
-
<!– timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 –>
-
<cache name=“passwordRetryCache”
-
maxEntriesLocalHeap=
“2000”
-
eternal=
“false”
-
timeToIdleSeconds=
“3600”
-
timeToLiveSeconds=
“0”
-
overflowToDisk=
“false”
-
statistics=
“true”>
-
</cache>
-
<!– 权限记录缓存 锁定1小时 –>
-
<cache name=“authorizationCache”
-
maxEntriesLocalHeap=
“2000”
-
eternal=
“false”
-
timeToIdleSeconds=
“3600”
-
timeToLiveSeconds=
“0”
-
overflowToDisk=
“false”
-
statistics=
“true”>
-
</cache>
-
<!– 登录认证记录缓存 锁定10分钟 –>
-
<cache name=“authenticationCache”
-
maxEntriesLocalHeap=
“2000”
-
eternal=
“false”
-
timeToIdleSeconds=
“3600”
-
timeToLiveSeconds=
“0”
-
overflowToDisk=
“false”
-
statistics=
“true”>
-
</cache>
-
<!– 会话次数缓存 –>
-
<cache name=“shiro-activeSessionCache”
-
maxEntriesLocalHeap=
“10000”
-
overflowToDisk=
“false”
-
eternal=
“false”
-
diskPersistent=
“false”
-
timeToLiveSeconds=
“0”
-
timeToIdleSeconds=
“0”
-
statistics=
“true”/>
-
-
</ehcache>
b、RetryLimitHashedCredentialsMatcher.java
-
package com.loan.credentials;
-
-
import java.util.concurrent.atomic.AtomicInteger;
-
-
import javax.annotation.Resource;
-
-
import org.apache.shiro.authc.AuthenticationInfo;
-
import org.apache.shiro.authc.AuthenticationToken;
-
import org.apache.shiro.authc.ExcessiveAttemptsException;
-
import org.apache.shiro.authc.SaltedAuthenticationInfo;
-
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-
import org.apache.shiro.cache.Cache;
-
import org.apache.shiro.cache.CacheManager;
-
import org.apache.shiro.crypto.hash.SimpleHash;
-
import org.apache.shiro.util.ByteSource;
-
-
-
/**
-
* <p>
-
* User: Zhang Kaitao
-
* <p>
-
* Date: 14-1-28
-
* <p>
-
* Version: 1.0
-
*/
-
public
class RetryLimitHashedCredentialsMatcher extends
-
HashedCredentialsMatcher {
-
//AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。
-
private Cache<String, AtomicInteger> passwordRetryCache;
-
-
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
-
passwordRetryCache = cacheManager.getCache(
“passwordRetryCache”);
-
}
-
//控制密码输入错误次数
-
@Override
-
public boolean doCredentialsMatch(AuthenticationToken token,
-
AuthenticationInfo info) {
-
String username = (String) token.getPrincipal();
-
// retry count + 1
-
AtomicInteger retryCount = passwordRetryCache.get(username);
-
System.out.println(
“retryCount:”+retryCount);
-
if (retryCount ==
null) {
-
retryCount =
new AtomicInteger(
0);
-
passwordRetryCache.put(username, retryCount);
-
}
-
if (retryCount.incrementAndGet() >
5) {
-
// if retry count > 5 throw
-
throw
new ExcessiveAttemptsException();
-
}
-
boolean matches =
super.doCredentialsMatch(token, info);
-
if (matches) {
-
// clear retry count
-
passwordRetryCache.remove(username);
-
}
-
-
return matches;
-
}
-
}
c、UserRealm.java
-
package com.loan.realm;
-
-
import org.apache.log4j.Logger;
-
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.subject.PrincipalCollection;
-
import org.apache.shiro.util.ByteSource;
-
-
import com.loan.entity.User;
-
import com.loan.pojo.UserParams;
-
import com.loan.service.ResourceService;
-
import com.loan.service.RoleService;
-
import com.loan.service.UserRoleService;
-
import com.loan.service.UserService;
-
-
-
/**
-
* @ClassName: UserRealm
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月5日 上午10:09:51
-
*
-
*/
-
public
class UserRealm extends AuthorizingRealm {
-
private
static
final Logger logger = Logger.getLogger(UserRealm.class);
-
-
@javax.annotation.Resource
-
private UserRoleService userRoleService;
-
@javax.annotation.Resource
-
private ResourceService resourceService;
-
@javax.annotation.Resource
-
private UserService userService;
-
@javax.annotation.Resource
-
private RoleService roleService;
-
@Override
-
protected AuthorizationInfo doGetAuthorizationInfo(
-
PrincipalCollection principals) {
-
//直接调用getPrimaryPrincipal得到之前传入的用户名
-
User user = (User) principals.getPrimaryPrincipal();
-
logger.info(
"[用户:" + user.getUsername() +
"|权限授权]");
-
SimpleAuthorizationInfo authorizationInfo =
new SimpleAuthorizationInfo();
-
//根据用户名调用UserService接口获取角色及权限信息
-
authorizationInfo.setRoles(roleService
-
.loadRoleIdByUsername(user.getUsername()));
-
authorizationInfo.setStringPermissions(resourceService
-
.loadPermissionsByUsername(user.getUsername()));
-
logger.info(
"[用户:" + user.getUsername() +
"|权限授权完成]");
-
return authorizationInfo;
-
}
-
-
@Override
-
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
-
// 获取基于用户名和密码的令牌
-
// 实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
-
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
-
String username = (String) token.getPrincipal();
-
System.out.println(
"pwd:"+token.getCredentials().toString());
-
logger.info(
"[用户:" + username +
"|系统权限认证]");
-
User u =
new User();
-
u.setUsername(username);
-
if (userService.find(
new UserParams(u)).size() >
0) {
-
User sqluser = userService.find(
new UserParams(u)).get(
0);
-
// 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
-
System.out.println(
"Realm:"+ByteSource.Util.bytes(sqluser.getCredentialsSalt()));
-
SimpleAuthenticationInfo authenticationInfo =
new SimpleAuthenticationInfo(sqluser, sqluser.getPassword(),
-
ByteSource.Util.bytes(sqluser.getCredentialsSalt()),
this.getName());
// realm
-
logger.info(
"[用户:" + username +
"|系统权限认证完成]");
-
return authenticationInfo;
-
}
-
return
null;
-
}
-
}
d、MySessionListener1(用户测试会话过期时间)
-
package com.loan.util;
-
import java.util.Date;
-
-
import org.apache.shiro.session.Session;
-
import org.apache.shiro.session.SessionListener;
-
-
public
class MySessionListener1 implements SessionListener {
-
@Override
-
public void onStart(Session session) {
//会话创建时触发
-
System.out.println(
"会话创建:" + session.getId()+
"》》时间:"+session.getLastAccessTime());
-
}
-
@Override
-
public void onExpiration(Session session) {
//会话过期时触发
-
System.out.println(
"会话过期:" + session.getId()+
"》》时间:"+
new Date());
-
}
-
@Override
-
public void onStop(Session session) {
//退出/会话过期时触发
-
System.out.println(
"会话停止:" + session.getId()+
"》》时间:"+session.getLastAccessTime());
-
}
-
}
e、KickoutSessionControlFilter.java
-
package com.loan.util;
-
-
import java.io.Serializable;
-
import java.util.Deque;
-
import java.util.LinkedList;
-
-
import javax.servlet.ServletRequest;
-
import javax.servlet.ServletResponse;
-
-
import org.apache.shiro.cache.Cache;
-
import org.apache.shiro.cache.CacheManager;
-
import org.apache.shiro.session.Session;
-
import org.apache.shiro.session.mgt.DefaultSessionKey;
-
import org.apache.shiro.session.mgt.SessionManager;
-
import org.apache.shiro.subject.Subject;
-
import org.apache.shiro.web.filter.AccessControlFilter;
-
import org.apache.shiro.web.util.WebUtils;
-
-
import com.loan.entity.User;
-
-
public
class KickoutSessionControlFilter extends AccessControlFilter{
-
private String kickoutUrl;
//踢出后到的地址
-
private
boolean kickoutAfter;
//踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
-
private
int maxSession;
//同一个帐号最大会话数 默认1
-
private SessionManager sessionManager;
-
private Cache<String, Deque<Serializable>> cache;
-
-
public void setKickoutUrl(String kickoutUrl) {
-
this.kickoutUrl = kickoutUrl;
-
}
-
-
public void setKickoutAfter(boolean kickoutAfter) {
-
this.kickoutAfter = kickoutAfter;
-
}
-
-
public void setMaxSession(int maxSession) {
-
this.maxSession = maxSession;
-
}
-
-
public void setSessionManager(SessionManager sessionManager) {
-
this.sessionManager = sessionManager;
-
}
-
-
public void setCacheManager(CacheManager cacheManager) {
-
this.cache = cacheManager.getCache(
"shiro-activeSessionCache");
-
}
-
/**
-
* 是否允许访问,返回true表示允许
-
*/
-
@Override
-
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
-
return
false;
-
}
-
/**
-
* 表示访问拒绝时是否自己处理,如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。
-
*/
-
@Override
-
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
-
Subject subject = getSubject(request, response);
-
if(!subject.isAuthenticated() && !subject.isRemembered()) {
-
//如果没有登录,直接进行之后的流程
-
return
true;
-
}
-
-
Session session = subject.getSession();
-
String username = ((User)(subject.getPrincipal())).getUsername();
-
Serializable sessionId = session.getId();
-
-
// 初始化用户的队列放到缓存里
-
Deque<Serializable> deque = cache.get(username);
-
if(deque ==
null) {
-
deque =
new LinkedList<Serializable>();
-
cache.put(username, deque);
-
}
-
-
//如果队列里没有此sessionId,且用户没有被踢出;放入队列
-
if(!deque.contains(sessionId) && session.getAttribute(
"kickout") ==
null) {
-
deque.push(sessionId);
-
}
-
//如果队列里的sessionId数超出最大会话数,开始踢人
-
while(deque.size() > maxSession) {
-
Serializable kickoutSessionId =
null;
-
if(kickoutAfter) {
//如果踢出后者
-
kickoutSessionId=deque.getFirst();
-
kickoutSessionId = deque.removeFirst();
-
}
else {
//否则踢出前者
-
kickoutSessionId = deque.removeLast();
-
}
-
try {
-
Session kickoutSession = sessionManager.getSession(
new DefaultSessionKey(kickoutSessionId));
-
if(kickoutSession !=
null) {
-
//设置会话的kickout属性表示踢出了
-
kickoutSession.setAttribute(
"kickout",
true);
-
}
-
}
catch (Exception e) {
//ignore exception
-
e.printStackTrace();
-
}
-
}
-
-
//如果被踢出了,直接退出,重定向到踢出后的地址
-
if (session.getAttribute(
"kickout") !=
null) {
-
//会话被踢出了
-
try {
-
subject.logout();
-
}
catch (Exception e) {
-
}
-
WebUtils.issueRedirect(request, response, kickoutUrl);
-
return
false;
-
}
-
return
true;
-
}
-
}
2、spring-mvc.xml中加入shiro注解配置
-
<!– 配置用于开启Shiro Spring AOP 权限注解的支持 –>
-
<bean
-
class=
“org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”
-
depends-on=
“lifecycleBeanPostProcessor” />
-
<aop:config proxy-target-class=“true”>
</aop:config>
-
<bean
-
class=
“org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor”>
-
<property name=“securityManager” ref=“securityManager” />
-
</bean>
-
<mvc:resources mapping=“/static/**” location=“/static/” cache-period=“2592000”/>
3、web.xml中加入过滤器shiroFilter
-
<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>/*
</url-pattern>
-
</filter-mapping>
四、基本配置已经完成,接下来通过简单的demo对逻辑进行梳理
1、登录验证逻辑
基本思路:新建用户(密码加密)->利用shiro的credentialsMatcher进行验证->完成登录(
重点:密码加密方式与credentialsMatcher的验证方式一致,当然你也可以自己实现验证)
(1)建立sys_user数据库表
(2)实体类User.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
import java.util.List;
-
-
import lombok.Data;
-
/**
-
* @ClassName: User
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月5日 下午2:31:58
-
*
-
*/
-
@SuppressWarnings(
“serial”)
-
@Data
-
public
class User implements Serializable {
-
private Long id;
-
private String username;
// 用户名
-
private String workNo;
// 工作编号
-
private String salt;
// 盐(密码安全)
-
private String password;
// 密码
-
private Integer age;
// 年龄
-
private String state;
// 状态
-
private Long orgId;
-
private String pic;
-
private String phone;
-
private String address;
-
private String email;
-
private Integer percent;
-
/**
-
* @Title: getCredentialsSalt
-
* @Description: salt = salt + username
-
* @param @return 设定文件
-
* @return String 返回类型
-
* @author jiayq
-
* @throws
-
*/
-
public String getCredentialsSalt() {
-
return username + salt;
-
}
-
-
}
(3)UserMapper.xml
-
<?xml version=“1.0” encoding=“UTF-8”?>
-
<!DOCTYPE mapper PUBLIC “-//mybatis.org//DTD Mapper 3.0//EN” “http://mybatis.org/dtd/mybatis-3-mapper.dtd”>
-
<mapper namespace=“com.loan.dao.mapper.UserMapper”>
-
<!–增加–>
-
<insert id=“save” parameterType=“com.loan.entity.User” keyColumn=“id” useGeneratedKeys=“true” keyProperty=“id”>
-
insert into sys_user(id,username,work_no,salt,password,age,state,org_id,pic,phone,address,email,percent) values (#{id},#{username},#{workNo},#{salt},#{password},#{age},#{state},#{orgId},#{pic},#{phone},#{address},#{email},#{percent})
-
</insert>
-
</mapper>
(4)UserMapper.java
-
package com.loan.dao.mapper;
-
-
import java.util.List;
-
-
import com.loan.entity.User;
-
import com.loan.pojo.UserParams;
-
-
-
/**
-
* @ClassName: UserMapper
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月29日 下午4:08:43
-
*
-
*/
-
public
interface UserMapper {
-
public void save(User user);
-
}
(5)UserService.java
-
package com.loan.service;
-
-
import java.util.List;
-
-
import com.loan.entity.User;
-
-
/**
-
* @ClassName: UserService
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月7日 上午10:09:32
-
*
-
*/
-
public
interface UserService {
-
-
public void save(User user);
-
-
}
(6)UserServiceImpl.java
-
package com.loan.service.impl;
-
-
import java.util.List;
-
-
import javax.annotation.Resource;
-
-
import org.springframework.stereotype.Service;
-
-
import com.loan.dao.mapper.UserMapper;
-
import com.loan.dao.mapper.UserRoleMapper;
-
import com.loan.entity.User;
-
import com.loan.pojo.UserParams;
-
import com.loan.service.UserService;
-
import com.loan.util.EndecryptUtils;
-
import com.loan.util.PasswordHelper;
-
/**
-
* @ClassName: UserServiceImpl
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月6日 上午9:24:49
-
*
-
*/
-
@Service(
"userService")
-
public
class UserServiceImpl implements UserService {
-
@Resource
-
private UserMapper userMapper;
-
@Resource
-
private UserRoleMapper userRoleMapper;
-
@Override
-
public void save(User user) {
-
User u=
new PasswordHelper().encryptPassword(user);
-
userMapper.save(u);
-
-
}
-
-
}
(7)PasswordHelper.java
-
package com.loan.util;
-
-
import org.apache.shiro.authc.SimpleAuthenticationInfo;
-
import org.apache.shiro.crypto.RandomNumberGenerator;
-
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
-
import org.apache.shiro.crypto.hash.SimpleHash;
-
import org.apache.shiro.util.ByteSource;
-
-
import com.loan.entity.User;
-
-
public
class PasswordHelper {
-
private RandomNumberGenerator randomNumberGenerator =
new SecureRandomNumberGenerator();
-
private String algorithmName =
“md5”;
-
private
final
int hashIterations =
2;
-
-
public User encryptPassword(User user) {
-
if(user.getSalt()==
null||(
“”).equals(user.getSalt())){
-
user.setSalt(randomNumberGenerator.nextBytes().toHex());
-
}
-
String newPassword =
new SimpleHash(algorithmName, user.getPassword(),
-
ByteSource.Util.bytes(user.getCredentialsSalt()), hashIterations).toHex();
-
user.setPassword(newPassword);
-
return user;
-
}
-
}
重点:
(8)新建用户
-
package com.loan.controller;
-
-
import javax.annotation.Resource;
-
-
import org.junit.Test;
-
import org.junit.runner.RunWith;
-
import org.springframework.stereotype.Controller;
-
import org.springframework.test.context.ContextConfiguration;
-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
import org.springframework.ui.Model;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
-
import com.loan.entity.User;
-
import com.loan.service.UserService;
-
-
@Controller
-
@RunWith(SpringJUnit4ClassRunner.class)
-
@ContextConfiguration(
“classpath:/application-context.xml”)
-
@RequestMapping(
“/test”)
-
public
class TestController {
-
@Resource
-
private UserService userService;
-
@Test
-
public void userCreate(){
-
User user=
new User();
-
user.setAge(
20);
-
user.setPassword(
“123”);
-
user.setUsername(
“shiroUser”);
-
userService.save(user);
-
}
-
}
右键run as JUnit test
(9)简单login.jsp页面进行登录
-
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml">
-
<head>
-
<meta charset="utf-8" />
-
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
-
<title>登录
</title>
-
<!-- Javascript -->
-
<script src="${pageContext.request.contextPath}/static/jquery-1.7.2.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//登录</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">login</span>(<span class="hljs-params"></span>) </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">if</span> ($(<span class="hljs-string">"#userName"</span>).val() == <span class="hljs-literal">null</span> || $(<span class="hljs-string">"#userName"</span>).val() == <span class="hljs-string">''</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> alert(<span class="hljs-string">"请输入用户名!"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ($(<span class="hljs-string">"#pwd"</span>).val() == <span class="hljs-literal">null</span> || $(<span class="hljs-string">"#pwd"</span>).val() == <span class="hljs-string">""</span>) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> alert(<span class="hljs-string">"密码不能为空!"</span>);</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> } <span class="hljs-keyword">else</span> {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"form"</span>).submit();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="20"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="21"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="22"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="23"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">//keydowm登录</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="24"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">keydown_login</span>(<span class="hljs-params"></span>) </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="25"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-built_in">document</span>.onkeydown = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="26"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">var</span> theEvent = <span class="hljs-built_in">window</span>.event || e;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="27"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">var</span> code = theEvent.keyCode || theEvent.which;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="28"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">if</span> (code == <span class="hljs-number">13</span> && (<span class="hljs-literal">null</span> != $(<span class="hljs-string">"#pwd"</span>).val())) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="29"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> theEvent.returnValue = <span class="hljs-literal">false</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="30"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> theEvent.cancel = <span class="hljs-literal">true</span>;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="31"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"form"</span>).submit();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="32"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="33"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="34"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="35"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"></<span class="hljs-name">script</span>></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="36"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"></<span class="hljs-name">head</span>></span> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="37"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">body</span>></span> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="38"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"${pageContext.request.contextPath}/loginTest">
-
<div class="form-group">
-
<label>Username
</label>
-
<input type="text" name="username" id="userName" />
-
<label style="color: red; font-size: 14px; float: right; margin-right: 35px;">${message}
</label>
-
</div>
-
<div class="form-group">
-
<label >Password
</label>
-
<input type="password" name="password" id="pwd" />
-
</div>
-
<div class="checkbox">
-
<label>
<input type="checkbox" name="rememberMe"/> RemeberMe
</label>
-
</div>
-
<button type="button" class="btn" onclick="login()">Sign in!
</button>
-
</form>
-
</body>
-
</html>
(10)LoginController.java
-
package com.loan.controller;
-
-
-
import javax.annotation.Resource;
-
import javax.servlet.http.HttpServletRequest;
-
-
import org.apache.shiro.SecurityUtils;
-
import org.apache.shiro.authc.AuthenticationException;
-
import org.apache.shiro.authc.ExcessiveAttemptsException;
-
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.stereotype.Controller;
-
import org.springframework.ui.Model;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.ResponseBody;
-
-
import com.loan.entity.User;
-
import com.loan.service.UserService;
-
/**
-
* @ClassName: LoginController
-
* @Description: TODO(登录controller)
-
* @author jiayq
-
* @date 2016年9月5日 下午5:06:33
-
*
-
*/
-
@Controller
-
@RequestMapping(
"/")
-
public
class LoginController {
-
@Resource
-
private UserService userService;
-
/**
-
* @Title: loginView
-
* @Description: TODO(转向登录界面)
-
* @param @return 设定文件
-
* @return String 返回类型
-
* @throws
-
*/
-
@RequestMapping(value =
"/login")
-
public String loginView(){
-
return
"login";
-
}
-
-
/**
-
* @Title: login1
-
* @Description: TODO(shiro+EndecryptUtils进行认证)
-
* @param @param request
-
* @param @param model
-
* @param @param username
-
* @param @param password
-
* @param @return 设定文件
-
* @return String 返回类型
-
* @throws
-
*/
-
@RequestMapping(value =
"/loginTest")
-
public String login(HttpServletRequest request, Model model, String username, String password,boolean rememberMe) {
-
System.out.println(
"rememberMe:"+rememberMe);
-
//将form中的用户名密码传入Realm 的doGetAuthenticationInfo
-
UsernamePasswordToken token =
new UsernamePasswordToken(username, password.toCharArray());
-
token.setRememberMe(rememberMe);
-
Subject currentUser = SecurityUtils.getSubject();
-
String error =
"";
-
try {
-
currentUser.login(token);
-
}
catch (UnknownAccountException ex) {
// 用户名没有找到
-
error =
"您输入的用户名不存在!";
-
}
catch (IncorrectCredentialsException ex) {
// 用户名密码不匹配
-
error =
"用户名密码不匹配 !";
-
}
-
catch(ExcessiveAttemptsException e){
-
error=
"密码错误次数已超五次,账号锁定1小时!";
-
}
-
catch (AuthenticationException ex) {
// 其他的登录错误
-
ex.printStackTrace();
-
error =
"其他的登录错误 !";
-
}
-
// 验证是否成功登录的方法
-
if (currentUser.isAuthenticated()) {
-
return
"redirect:/admin/index";
-
}
else {
-
model.addAttribute(
"message", error);
-
currentUser.logout();
-
return
"login";
-
}
-
-
}
-
-
-
}
结果:
重点逻辑:
五、密码输入错误账号锁定功能
前面给的代码中已经实现,主要梳理下逻辑:
六、并发登录人数控制
代码上头已经给出,主要梳理流程:
七、记住密码
spring-shiro.xml中相关配置:
jsp:
LoginController.java
八、shiro的权限授权
1、建立用户、角色、资源以及之间的相关表
(1)sys_user:
User.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
import java.util.List;
-
-
import lombok.Data;
-
/**
-
* @ClassName: User
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月5日 下午2:31:58
-
*
-
*/
-
@SuppressWarnings(
“serial”)
-
@Data
-
public
class User implements Serializable {
-
private Long id;
-
private String username;
// 用户名
-
private String workNo;
// 工作编号
-
private String salt;
// 盐(密码安全)
-
private String password;
// 密码
-
private Integer age;
// 年龄
-
private String state;
// 状态
-
private Long orgId;
-
private String pic;
-
private String phone;
-
private String address;
-
private String email;
-
private Integer percent;
-
/**
-
* @Title: getCredentialsSalt
-
* @Description: salt = salt + username
-
* @param @return 设定文件
-
* @return String 返回类型
-
* @author jiayq
-
* @throws
-
*/
-
public String getCredentialsSalt() {
-
return username + salt;
-
}
-
-
}
-
(2)sys_user_role:
UserRole.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
-
import lombok.Data;
-
/**
-
* @ClassName: UserRole
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月20日 上午10:17:50
-
*
-
*/
-
@SuppressWarnings(
“serial”)
-
@Data
-
public
class UserRole implements Serializable {
-
private Long id;
-
private Long roleId;
//角色id
-
private Long userId;
//用户id
-
-
}
-
(3)sys_role:
Role.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
-
import lombok.Data;
-
/**
-
* @ClassName: Role
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月20日 上午10:15:29
-
*
-
*/
-
@SuppressWarnings(
“serial”)
-
@Data
-
public
class Role implements Serializable{
-
private Long id;
-
private String name;
//名称
-
private String description;
//描述
-
private String state;
//状态
-
private String code;
//编码
-
private Long pid ;
//父id
-
private String remark;
//备注
-
//关联资源列表
-
private String resources;
-
-
-
}
-
(4)sys_resource:
Resource.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
import lombok.Data;
-
/**
-
* @ClassName: Resource
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月19日 下午4:57:11
-
*
-
*/
-
@SuppressWarnings(
“serial”)
-
@Data
-
public
class Resource implements Serializable{
-
private Long id;
-
private String name;
//名称
-
//private ResourceType type = ResourceType.menu;//资源类型
-
private String type;
-
private Integer leaf;
//0表示是叶子节点
-
private Long priority;
//顺序
-
private Long pid;
//父id
-
private String permission;
//权限
-
private String status;
//状态
-
private String url;
//路径
-
private String outUrl;
//站外路径
-
private String pic;
-
//关联的角色列表
-
private List<Role> roles;
-
private List<Resource> children =
new ArrayList<Resource>();
-
}
-
(5)sys_role_resource:
RoleResource.java
-
package com.loan.entity;
-
-
import java.io.Serializable;
-
-
import lombok.Data;
-
-
/**
-
* @ClassName: RoleResource
-
* @Description: TODO(这里用一句话描述这个类的作用)
-
* @author jiayq
-
* @date 2016年9月20日 上午10:16:57
-
*
-
*/
-
@Data
-
public
class RoleResource implements Serializable{
-
private Long id;
-
private Long roleId;
//角色id
-
private Long resourceId;
//资源id
-
-
}
-
2、新建角色Role为角色分配资源Resource(权限)
(1)新建资源:
-
@Test
-
public void resourceCreate(){
-
//建立三个资源
-
for(
int i=
1;i<
4;i++){
-
com.loan.entity.Resource resource=
new com.loan.entity.Resource();
-
resource.setName(
“resourceTest_”+i);
-
resource.setPermission(
“test:permission_”+i);
-
resourceService.save(resource);
-
}
-
}
(2)新建role
-
@Test
-
public void roleCreate(){
-
Role role=
new Role();
-
role.setName(
"testRole");
-
roleService.save(role);
-
for(
int i=
143;i<
146;i++){
-
RoleResource roleResource=
new RoleResource();
-
roleResource.setRoleId(role.getId());
-
roleResource.setResourceId((
long)i);
-
roleResourceService.save(roleResource);
-
}
-
}
(3)新建user
-
@Test
-
public void UserCreate(){
-
User user=
new User();
-
user.setAge(
20);
-
user.setPassword(
"123");
-
user.setUsername(
"shiroUser1");
-
userService.save(user);
-
UserRole ur=
new UserRole();
-
ur.setRoleId((
long)
25);
-
ur.setUserId(user.getId());
-
userRoleService.save(ur);
-
}
至此,所以关系逻辑建立。
3、UserRealm权限授权
-
@Override
-
protected AuthorizationInfo doGetAuthorizationInfo(
-
PrincipalCollection principals) {
-
//直接调用getPrimaryPrincipal得到之前传入的用户名
-
User user = (User) principals.getPrimaryPrincipal();
-
logger.info(
"[用户:" + user.getUsername() +
"|权限授权]");
-
SimpleAuthorizationInfo authorizationInfo =
new SimpleAuthorizationInfo();
-
//根据用户名调用UserService接口获取角色及权限信息
-
authorizationInfo.setRoles(roleService
-
.loadRoleIdByUsername(user.getUsername()));
-
authorizationInfo.setStringPermissions(resourceService
-
.loadPermissionsByUsername(user.getUsername()));
-
logger.info(
"[用户:" + user.getUsername() +
"|权限授权完成]");
-
return authorizationInfo;
-
}
roleMapper.xml
-
<select id="loadRoleIdByUsername" parameterType="java.lang.String" resultMap="Role_resultMap">
-
select sr.* from sys_role sr,sys_user su,sys_user_role sur WHERE su.username=#{username} and su.id=sur.user_id and sr.id=sur.role_id
-
</select>
resourceMapper.xml
-
<select id="loadPermissionByUsername" parameterType="java.lang.String" resultMap="Resource_resultMap">
-
SELECT sres.* from sys_user su,sys_user_role sur,sys_role sr,sys_role_resource srr,sys_resource sres WHERE su.username=#{name} and su.id=sur.user_id and sr.id=sur.role_id AND srr.role_id=sr.id and srr.resource_id=sres.id
-
</select>
3、通过jsp/注释测试
(1)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>test
</title>
-
<script src="${pageContext.request.contextPath}/static/jquery-1.7.2.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logout</span>(<span class="hljs-params"></span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> $.post(<span class="hljs-string">'${pageContext.request.contextPath}/logout'</span>,<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> location.reload();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> });</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-tag"></<span class="hljs-name">script</span>></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-tag"></<span class="hljs-name">head</span>></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="18"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-tag"><<span class="hljs-name">body</span>></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="19"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-tag"><<span class="hljs-name">shiro:guest</span>></span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="20"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">欢迎游客访问,<span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"${pageContext.request.contextPath}/login">登录
</a>
-
</shiro:guest>
-
<shiro:user>
-
欢迎
<shiro:principal property="username"/>登录,
<a href="javascript:logout();">退出
</a>
-
</shiro:user>
-
<br/>
-
<shiro:authenticated>
-
用户[
<shiro:principal property="username"/>]已身份验证通过
-
</shiro:authenticated>
-
<br/>
-
<shiro:hasRole name="testRole">
-
用户[
<shiro:principal property="username"/>]拥有testRole角色
<br/>
-
</shiro:hasRole>
-
<br/>
-
<shiro:hasPermission name="test:permission_1">
-
用户[
<shiro:principal property="username"/>]拥有权限test:permission_1
<br/>
-
</shiro:hasPermission>
-
</body>
-
</html>
登录结果:
(2)通过注解控制方法的访问:
-
@RequiresPermissions(
"test:permission_1")
-
@RequestMapping(
"/testAnnotation")
-
public String testAnnotation(){
-
return
"/index";
-
}
-
-
@RequiresPermissions(
"test:nopermission")
-
@RequestMapping(
"/testAnnotation1")
-
public String testAnnotation1(){
-
return
"index";
-
}
测试结果:
终于完了!!!
源代码下载:http://download.csdn.net/download/dreamer_8399/9959021