Shiro概述
1、什么是Shiro
Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与Web 集成、缓存等。
2、为什么要学shiro
既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
3、基本功能

【Authentication】
身份认证/登录,验证用户是不是拥有相应的身份;
【Authorization】
授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
【Session Manager】
会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信
【Cryptography】
加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
【Web Support】
Web 支持,可以非常容易的集成到Web 环境;
【Caching】
缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
【Concurrency】
shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;
【Testing】
提供测试支持;
【Run As】
允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
【Remember Me】
记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
注意:Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过 相应的接口注入给Shiro即可
4,架构说明

【Subject】
Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
【SecurityManager】
SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。
【Authenticator】
Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。
【Authorizer】
Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
【realm】
Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据
注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。
【sessionManager】
sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
【SessionDAO】
SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。
【CacheManager】
CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。
【Cryptography】
Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。
ssm+shrio集成
准备工作
- sql文件
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`perid` int(11) NOT NULL AUTO_INCREMENT,
`pername` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`percode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`perid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户查询', 'user:query');
INSERT INTO `permission` VALUES (2, '用户添加', 'user:add');
INSERT INTO `permission` VALUES (3, '用户修改', 'user:update');
INSERT INTO `permission` VALUES (4, '用户删除', 'user:delete');
INSERT INTO `permission` VALUES (5, '导出用户', 'user:export');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`roleid` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`roleid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员');
INSERT INTO `role` VALUES (2, 'CEO');
INSERT INTO `role` VALUES (3, '保安');
-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
`perid` int(255) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`perid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 2);
INSERT INTO `role_permission` VALUES (3, 1);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (4, 1);
INSERT INTO `role_permission` VALUES (5, 3);
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`userpwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', '639ffb0cbcca39d4fff8348844b1974e', '男', '武汉');
INSERT INTO `user` VALUES (2, 'lisi', '0d303fa8e2e2ca98555f23a731a58dd9', '女', '北京');
INSERT INTO `user` VALUES (3, 'wangwu', '473c41db9af5cc0d90e7adfd2b6d9180', '女', '成都');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`userid` int(11) NOT NULL,
`roleid` int(11) NOT NULL,
PRIMARY KEY (`userid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (2, 2);
INSERT INTO `user_role` VALUES (3, 3);
SET FOREIGN_KEY_CHECKS = 1;
数据结构
张三 -->user:query user:add user:update user:delete 李四 -->user:query user:add user:update 王五 -->user:query user:export
ssm环境搭建
- IDEA创建WEB程序:ssm_shiro
- pom.xml
<!-- 声明常量 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<servlet.version>3.1.0</servlet.version>
<jsp.version>2.3.1</jsp.version>
<spring.version>4.3.24.RELEASE</spring.version>
<mybatis.version>3.5.1</mybatis.version>
<mybatis.spring.version>2.0.1</mybatis.spring.version>
<mysql.version>5.1.47</mysql.version>
<pagehelper.version>5.1.10</pagehelper.version>
<druid.version>1.1.19</druid.version>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.26</slf4j.version>
<jackson.version>2.9.9</jackson.version>
<fastjson.version>1.2.60</fastjson.version>
<shiro.version>1.4.1</shiro.version>
</properties>
<dependencies>
<!--servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- javax.servlet.jsp -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>
<!--spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- 引入shiro的包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
<build>
<finalName>ssm_shiro_separate</finalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 配置urlencoding -->
<uriEncoding>UTF-8</uriEncoding>
<!-- 配置端口 -->
<port>8080</port>
<!-- 配置访问路径 -->
<path>/ssm_shiro_separate</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
- application-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 解析配置文件 -->
<context:property-placeholder location="classpath:db.properties" system-properties-mode="FALLBACK"/>
<!-- 声明数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="maxActive" value="${maxActive}"></property>
<property name="initialSize" value="${initialSize}"></property>
<property name="maxWait" value="${maxWait}"></property>
<property name="minIdle" value="${minIdle}"></property>
<property name="filters" value="${filters}"></property>
</bean>
<!-- 创建mybatis的 configuration对象-->
<bean id="configuration" class="org.apache.ibatis.session.Configuration">
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
</bean>
<!-- 创建sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 注入配置类 -->
<property name="configuration" ref="configuration"></property>
<!-- 扫描mapper.xml -->
<property name="mapperLocations">
<array>
<value>classpath:mapper/*Mapper.xml</value>
</array>
</property>
<!-- 配置分页插件 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor"></bean>
</array>
</property>
</bean>
<!-- 配置mapper接口的扫描 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zy.mapper"></property>
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
- application-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描 -->
<context:component-scan base-package="com.zy.service.impl"></context:component-scan>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的传播特性 -->
<tx:advice id="advise" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="reset*" propagation="REQUIRED"/>
<tx:method name="change*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<aop:pointcut expression="execution(* com.zy.service.impl.*.*(..))" id="pc"/>
<!-- 织入操作 -->
<aop:advisor advice-ref="advise" pointcut-ref="pc"/>
</aop:config>
</beans>
- application-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 声明凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--MD5加密-->
<property name="hashAlgorithmName" value="md5"></property>
<!--2次加密-->
<property name="hashIterations" value="2"></property>
</bean>
<!-- 声明userRealm -->
<bean id="userRealm" class="com.zy.realm.UserRealm">
<!-- 注入凭证匹配器 -->
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
<!-- 配置SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 注入realm -->
<property name="realm" ref="userRealm"></property>
</bean>
<!-- 配置shiro的过滤器 这里面的id必须和web.xml里面的配置一样 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 注入安全管理器 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 配置过滤器链 -->
<property name="filterChainDefinitions">
<value>
<!-- 放行index.jsp -->
/index.jsp*=anon
<!-- 放行跳转到登陆页面的路径 -->
/login/toLogin*=anon
<!-- 放行登陆的请求 -->
/login/login*=anon
<!-- 设置登出的路径 -->
/login/logout*=logout
<!-- 设置其它路径全部拦截 -->
/**=authc
</value>
</property>
<property name="filters">
<map>
<entry key="authc">
<bean class="com.zy.filter.ShiroLoginFilter"></bean>
</entry>
</map>
</property>
</bean>
</beans>
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:application-dao.xml"/>
<import resource="classpath:application-service.xml"/>
<import resource="classpath:application-shiro.xml"/>
</beans>
- db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
user=root
password=521521
#maxActive
maxActive=20
#initialSize
initialSize=1
#maxWait
maxWait=60000
#minIdle
minIdle=1
filters=stat,log4j,wall
- log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 扫描 -->
<context:component-scan base-package="com.zy.controller"></context:component-scan>
<!--扫描全局监控-->
<context:component-scan base-package="com.zy.handler"></context:component-scan>
<!-- 配置适配器和映射器 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/view/"></property>
<!-- 后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置文件上传 -->
<!-- 配置拦截器 -->
<!-- 配置静态资源放行-->
<mvc:default-servlet-handler/>
<!-- 启动Shrio的注解 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>ssm_shiro</display-name>
<!-- 配置shiro的集成开始 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 这里面的shiroFilter必须和application-shiro.xml里面的
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" >id 一样 -->
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<!-- 配置shiro的集成结束 -->
<!-- 配置编码过滤器 开始 -->
<filter>
<filter-name>encodingFilter</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>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<!-- 配置编码过滤器 结束 -->
<!-- 配置spring的监听器加载 applicationContext.xml开始 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<!-- 配置spring的监听器加载 applicationContext.xml结束 -->
<!-- 配置前端控制器开始 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 注入springmvc.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc.xml</param-value>
</init-param>
<!-- 启动创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- 配置前端控制器结束 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
环境搭建完成: 测试不报错就证明环境搭建完成
创建包:
- controller包
LoginController
@RestController
@RequestMapping("login")
public class LoginController {
/**
* 做登录
*
* @param username
* @param pwd
* @param session
* @return
*/
@RequestMapping("login")
public ResultObj login(String username, String pwd, HttpSession session) {
//得到主体
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
try {
subject.login(token);
System.out.println("登录成功");
ActivierUser activierUser = (ActivierUser) subject.getPrincipal();
session.setAttribute("user", activierUser.getUser());
return ResultObj.LOGIN_SUCCESS;
} catch (AuthenticationException e) {
e.printStackTrace();
return ResultObj.LOGIN_ERROR;
}
}
}
UserController
@Controller
@RequestMapping("user")
public class UserController {
@RequiresPermissions(value = "user:query")
@RequestMapping("query")
@ResponseBody
public Map<String, Object> query() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "query");
return map;
}
@RequiresPermissions(value = "user:add")
@RequestMapping("add")
@ResponseBody
public Map<String, Object> add() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "add");
return map;
}
@RequiresPermissions(value = "user:update")
@RequestMapping("update")
@ResponseBody
public Map<String, Object> update() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "update");
return map;
}
@RequiresPermissions(value = "user:delete")
@RequestMapping("delete")
@ResponseBody
public Map<String, Object> delete() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "delete");
return map;
}
@RequiresPermissions(value = "user:export")
@RequestMapping("export")
@ResponseBody
public Map<String, Object> export() {
Map<String, Object> map = new HashMap<>();
map.put("msg", "export");
return map;
}
}
- domain包
Permission
@Data
public class Permission {
private Integer perid;
private String pername;
private String percode;
}
Role
@Data
public class Role {
private Integer roleid;
private String rolename;
}
User
@Data
public class User {
private Integer userid;
private String username;
private String userpwd;
private String sex;
private String address;
}
- filter包
ShiroLoginFilter
public class ShiroLoginFilter extends FormAuthenticationFilter {
/**
* 如果用户为登录回调此方法
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse resp = (HttpServletResponse) response;
ResultObj resultObj = ResultObj.UN_LOGIN;
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
out.write(JSON.toJSONString(resultObj));
out.flush();
out.close();
return false;
}
}
- handler包
GlobalExceptionHandlerAdvise
@RestControllerAdvice
public class GlobalExceptionHandlerAdvise {
/**
* 未授权
* 只要当前项目的代码抛出UnauthenticatedException就会回调
*
* @return
*/
@ExceptionHandler(value = {UnauthorizedException.class})
public ResultObj unauthorized() {
return ResultObj.UNAUTHORIZED;
}
}
- mapper包
PermissionMapper
public interface PermissionMapper {
/**
* 根据Id查询权限
* @param userId
* @return
*/
List<Permission> queryPermissionByUserId(Integer userId);
}
RoleMapper
public interface RoleMapper {
/**
* 根据ID查询角色
* @param userId
* @return
*/
List<Role> queryRolesByUserId(Integer userId);
}
UserMapper
public interface UserMapper {
/**
* 根据用户名查询用户
* @param username
* @return
*/
User queryUserByUserName(String username);
}
- realm包
UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Override
public String getName() {
return this.getClass().getSimpleName();
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = token.getPrincipal().toString();
// 根据用户名查询用户
User user = this.userService.queryUserByUserName(username);
if (null != user) {
//查询角色
List<String> roles = this.roleService.queryRolesByUserId(user.getUserid());
//查询权限
List<String> permissions = this.permissionService.queryPermissionByUserId(user.getUserid());
//构造ActiverUser
ActivierUser activierUser = new ActivierUser(user, roles, permissions);
//创建盐
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername() + user.getAddress());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(activierUser, user.getUserpwd(), credentialsSalt, this.getName());
return info;
} else {
return null;
}
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ActivierUser activierUser = (ActivierUser) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<String> roles = activierUser.getRoles();
List<String> permissions = activierUser.getPermissions();
if (null != roles && roles.size() > 0) {
info.addRoles(roles);
}
if (null != permissions && permissions.size() > 0) {
info.addStringPermissions(permissions);
}
return info;
}
}
- impl包
PermissionServiceImpl
@Service
public class PermissionServiceImpl implements PermissionService {
@Autowired
private PermissionMapper permissionMapper;
@Override
public List<String> queryPermissionByUserId(Integer userId) {
List<Permission> list = permissionMapper.queryPermissionByUserId(userId);
List<String> permissions = new ArrayList<>();
for (Permission permission : list) {
permissions.add(permission.getPercode());
}
return permissions;
}
}
RoleServiceImpl
@Service
public class RoleServiceImpl implements RoleService {
@Autowired
private RoleMapper roleMapper;
@Override
public List<String> queryRolesByUserId(Integer userId) {
List<Role> list = roleMapper.queryRolesByUserId(userId);
ArrayList<String> roles = new ArrayList<>();
for (Role role : list) {
roles.add(role.getRolename());
}
return roles;
}
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByUserName(String username) {
return userMapper.queryUserByUserName(username);
}
}
- service包
PermissionService
public interface PermissionService {
/**
* 根据ID查询权限
* @param userId
* @return
*/
List<String> queryPermissionByUserId(Integer userId);
}
RoleService
public interface RoleService {
/**
* 根据ID查询角色
* @param userId
* @return
*/
List<String> queryRolesByUserId(Integer userId);
}
UserService
public interface UserService {
/**
* 根据用户名查询用户
* @param username
* @return
*/
User queryUserByUserName(String username);
}
- utils包
ActivierUser
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ActivierUser {
private User user;
private List<String> roles;
private List<String> permissions;
}
ResultObj
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultObj {
public static final ResultObj UN_LOGIN = new ResultObj("403", "未登陆");
public static final ResultObj UNAUTHORIZED = new ResultObj("405", "未授权");
public static ResultObj LOGIN_SUCCESS=new ResultObj("200", "登陆成功");
public static ResultObj LOGIN_ERROR=new ResultObj("-1","登陆失败");
private String code;
private String msg;
}
测试
未登陆时调用 http://localhost:8080/ssm_shiro_separate/user/query.action
|–显示 未登陆
使用张三登陆 调用http://localhost:8080/ssm_shiro_separate/login/login.action?username=zhangsan&&pwd=123456
|–显示 登陆成功
使用张三已登陆 调用http://localhost:8080/ssm_shiro_separate/user/export.action
|–显示 未授权