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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.sx</groupId> <artifactId>shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <spring.version>4.3.2.RELEASE</spring.version> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.17</log4j.version> <shiro.version>1.2.3</shiro.version> </properties> <dependencies> <!-- spring --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.4</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!-- log4j日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.4</version> </dependency> <!-- mybatis和spring集成包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.0</version> </dependency> <!-- 加入servlet和jsp的依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.2.1</version> <scope>provided</scope> </dependency> <!-- 引入shiro框架的依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>${shiro.version}</version> </dependency> <!-- shiro和spring集成包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <!-- MySQL数据库驱动依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build> </project>
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_2_5.xsd" version="2.5"> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <!-- 将spring管理的shiro框架生命周期交给Servlet --> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> <!-- 设置Spring容器中Filter对应Bean的id,可以不设,如果不设,必须保证当前Filter的name和Springbean的id值一致 --> <init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- springMVC前端控制器 --> <servlet> <servlet-name>MVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>MVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
shiro.xml
#配置自定义realm #类似于 spring 的 <bean id="" class=""> #自定义realm名称=自定义realm的全限定名 customRealm=cn.sxt.CustomRealm #定义凭证器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher #算法 credentialsMatcher.hashAlgorithmName=md5 #散列次数 credentialsMatcher.hashIterations=3 #设置realm的凭证匹配器 customRealm.credentialsMatcher=$credentialsMatcher #将自定义realm设置给SecurityManager的realm属性(类型Spring的依赖注入) securityManager.realms=$customRealm
重写realm
package cn.sxt; import java.util.Arrays; import java.util.List; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.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; public class CustomRealm extends AuthorizingRealm{ /* * 认证方法,开发者在方法内部自定认证的规则 * * token :令牌,在 主体login 传递过来的 * return AuthenticationInfo * 返回认证信息 * 如果返回null,认为认证失败 * */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { /* * 认证思路 * 1.获取 token令牌的身份(账号) * 2.在当前类中注入 UserService,调用serice的根据账号去数据库查询用户方法 * service调用Mapper/Dao层的根据账号查询用户方法 * 2.1 如果没有此用户,返回null,当前认证方法也返回null * 2.1 如果有此用户,把用户的密码和token的凭证(密码)进行匹配 * 2.2.1,匹配不成功,当前认证方法也返回null * 2.2.1 匹配成功,创建一个AuthenticationInfo 认证信息对象,认证成功 */ //1.获取 token令牌的身份(账号) String username = (String) token.getPrincipal(); /*User user=UserService.selectByUserName(username); * if(user !=null){ *进一步比对 * } * * */ //模拟数据库中的账号 List<String> usernames = Arrays.asList("list","admin","hello"); if(usernames.contains(username)) { //身份(账号)匹配成功,进一步匹配凭证(密码) System.out.println("账号匹配成功"); //String password = user.getPassword(); //(模拟数据库中的密码) abc + sen +散列三次后的密码 String hashedCredentials="5585e2acfff82f34259391863c714c45"; //数据库中的盐 ByteSource credentialsSalt = ByteSource.Util.bytes("sen"); //.创建返回认证信息的对象 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, hashedCredentials, credentialsSalt,this.getName()); return simpleAuthenticationInfo; } return null; } /* * 授权方法 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Object username = principals.getPrimaryPrincipal(); System.out.println(username); /* * 授权思路 * 1.通过当前认证的身份去数据库里面查询出当前身份对应的角色--》对应的权限 * 注入RoleService * Role role = roleService.selectByPrimarykey(roleId); * role.permissionIds = 10,1,13,15,16,17,11,18,19,20,21,12,22,23,24,25 * List<String> permissionExpressions = permission.selectExpressionsByIds(权限id数组集合) * 例如 * user:lsit * user:create * student:list * 等等 * 2. 将当前身份 对应的角色对应的所有权限设置给Shiro 授权信息对象 * 3. 程序运行shiro会自动判断当前身份是否有权限 */ //模拟数据库查询权限 List<String> asList = Arrays.asList("user:list","user:insert"); //创建一个授权信息对象 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //将权限添加到shiro 授权信息对象:不能为空null simpleAuthorizationInfo.addStringPermissions(asList); System.out.println("CustomRealm.doGetAuthorizationInfo()"); return simpleAuthorizationInfo; } }
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!-- shiro为集成springMvc 拦截异常 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!-- 没有权限异常跳转的页面 对应的页面在视图解析器下面 前缀+逻辑视图名称+后缀 --> <prop key="org.apache.shiro.authz.UnauthorizedException">unauthorized</prop> </props> </property> </bean> <context:component-scan base-package="cn.sxt"/> <mvc:annotation-driven/> <mvc:default-servlet-handler/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
springshiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.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.xsd "> <aop:config proxy-target-class="true" /> <!-- 开启aop,对代理类 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"></property> </bean> <!-- 配置Shiro框架的过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入安全管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 认证通过以后访问的页面 一般都是 后台首页 --> <property name="successUrl" value="/index.do" /> <!-- 自定义Filter --> <property name="filters"> <map> <!-- 使用自定义的表单认证过滤器--> <entry key="authc" value-ref="formAuthenticationFilter"></entry> </map> </property> <!-- 认证失败以后跳转的页面 /user/login.do --> <property name="loginUrl" value="/user/login.do" /> <!-- 没有权限访问时候提示页面 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <!-- shiro框架底层是多个过滤器,每个过滤都会有各自的职责 我们需要根据实际情况配置我们的过滤器链 配置规则 /资源 = 过滤器别名 /资源=过滤器别名 /** 所有资源 anon (org.apache.shiro.web.filter.authc.AnonymousFilter) 匿名访问过滤器,所有人都能方 authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 表单认证过滤器 ,只有认证通过以后才能访问资源 logout org.apache.shiro.web.filter.authc.LogoutFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 授权过滤器 语法 : /资源 = perms["权限"] user org.apache.shiro.web.filter.authc.UserFilter 设置记住我以后默认的访问页面 --> <property name="filterChainDefinitions"> <value> <!-- 过滤器代码从上到下执行,上面匹配,不会二次配置 --> <!-- 登录页面匿名访问 --> /login.jsp = anon <!-- 各种静态资源 css js image图片 --> /css/** = anon /js/** = anon /images/** = anon <!-- 访问路径=需要什么权限访问 /index.do=perms[user:list] --> <!-- 配置记住我访问的页面 --> /index.do=user <!-- 退出登录 --> /user/logout.do = logout <!-- 所有资源都需要认证才能访问 --> /** = authc </value> </property> </bean> <!-- 配置安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入 自定义realm --> <property name="realm" ref="customRealm"></property> <!-- 注入缓存管理器 --> <property name="cacheManager" ref="cacheManager"></property> <!-- 注入Session会话管理器 --> <property name="sessionManager" ref="sessionManager"></property> <!-- 记住我 --> <property name="rememberMeManager" ref="rememberMeManager"></property> </bean> <!-- 自定义表单认证过滤器 --> <bean id="formAuthenticationFilter" class="cn.sxt.MyFormAuthenticationFilter"> <!-- 设置表单提交的账号表单名称 --> <property name="usernameParam" value="name"/> <!-- 设置表单提交的账号表单名称 --> <property name="passwordParam" value="pwd"/> <!-- 设置表单提交的账号表单名称 --> <property name="rememberMeParam" value="rememberMe"/> </bean> <!-- 配置记住我 --> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <!-- 设置cookie信息 --> <property name="cookie"> <bean class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- 使用构造器设置cookie名称,保存到本地浏览器的key --> <constructor-arg value="rememberMe"></constructor-arg> <!-- 设置最大有效期 :单位秒 --> <property name="maxAge" value="#{3600 * 24 * 3}" /> </bean> </property> </bean> <!-- 配置Sesion会话管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 设置session的失效时长,单位毫秒 --> <property name="globalSessionTimeout" value="#{1000*3600}"></property> <!-- 删除失效的session --> <property name="deleteInvalidSessions" value="true"></property> </bean> <!-- 配置缓存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" /> </bean> <!-- 配置自定义CustomRealm --> <bean id="customRealm" class="cn.sxt.CustomRealm"> <!-- 注入凭证(密码)匹配器 --> <property name="credentialsMatcher" ref="credentialsMatcher"></property> </bean> <!-- 配置凭证匹配器 --> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!-- 加密方式 --> <property name="hashAlgorithmName" value="md5"></property> <!-- 散列次数 --> <property name="hashIterations" value="3"></property> </bean> </beans>
shiro-ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!--diskStore:缓存数据持久化的目录 地址 --> <diskStore path="D:developehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
自定义表单认证过滤器
package cn.sxt; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; public class MyFormAuthenticationFilter extends FormAuthenticationFilter{ }
登录后台代码
package cn.sxt.contorller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { @RequestMapping("/index") public String index() { return "index"; } }
package cn.sxt.contorller; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/user") public class UserContorller { @Scope("request") @RequestMapping("/list") @RequiresPermissions("user:list") public String list() { return "list"; } @RequestMapping("/login") public String login(HttpServletRequest request,Model m) { //获取认证失败的错误信息,在Shiro框架的 FormAuthenticationFilter 过滤器中共享 // 共享的属性名称 shiroLoginFailure // 共享的 shiro 异常的字节码 String shiroLoginFailure = (String) request.getAttribute("shiroLoginFailure"); if(shiroLoginFailure !=null) { if(UnknownAccountException.class.getName().equals(shiroLoginFailure)) { m.addAttribute("erroyMsg", "亲。账号不存在"); }else if(IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)) { m.addAttribute("erroyMsg", "亲。密码错误"); } } return "forward:/login.jsp"; } @RequestMapping("/logout") public String logout() { //使用shiro之前,清除Cookie和Session return "redirect:/login.jsp"; } }
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h3>登录页面</h3> <span style="color: red">${erroyMsg}</span> <form action="${pageContext.request.contextPath}/user/login.do" method="post"> 账号:<input name="name"/><br> 密码:<input name="pwd" type="password"/><br> 记住我:<input type="checkbox" name="rememberMe"><br> <button type="submit">登录</button> </form> </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 亲,没有权限访问 </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!-- 引入shiro标签 --> <%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 后台首页<br> <%-- 判断当前认证的身份是否拥有指定的权限 如果有权限,执行标签体中的代码,如果没有权限,不执行 <shiro:hasPermission name="user:list"> 标签体 </shiro:hasPermission> --%> <shiro:hasPermission name="user:list"> <a href="${pageContext.request.contextPath}/user/list.do">用户列表</a><br> </shiro:hasPermission> 欢迎,<shiro:principal></shiro:principal><a style="float: right;" href="${pageContext.request.contextPath}/user/logout.do">退出登录</a> </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 用户列表 </body> </html>