shiro和spring和springmvc的集成

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:\develop\ehcache" />
    
    <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>

猜你喜欢

转载自www.cnblogs.com/406070989senlin/p/11228816.html
今日推荐