zoukankan      html  css  js  c++  java
  • 007-shiro与spring web项目整合【一】基础搭建

    一、需求

      将原来基于url的工程改成使用shiro实现

    二、代码

      https://github.com/bjlhx15/shiro.git 中的permission_shiro

    三、去除原项目拦截器

      去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor拦截器。<mvc:interceptors>

    四、pom  

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.lhx.shiro</groupId>
        <artifactId>permission-shiro</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <spring.version>4.3.7.RELEASE</spring.version>
            <shiro.version>1.3.2</shiro.version>
        </properties>
        <dependencies>
            <!-- 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-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-quartz</artifactId>
                <version>${shiro.version}</version>
            </dependency>
    
            <!-- shiro 结束 -->
    
            <dependency>
                <groupId>commons-beanutils</groupId>
                <artifactId>commons-beanutils</artifactId>
                <version>1.8.3</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
                <version>1.1.0.Final</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>5.2.4.Final</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</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-web</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</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>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.38</version>
            </dependency>
            <dependency>
                <groupId>commons-dbcp</groupId>
                <artifactId>commons-dbcp</artifactId>
                <version>1.4</version>
            </dependency>
            <dependency>
                <groupId>commons-pool</groupId>
                <artifactId>commons-pool</artifactId>
                <version>1.6</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.3.0</version>
            </dependency>
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.1</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.4</version>
            </dependency>
    
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>taglibs</groupId>
                <artifactId>standard</artifactId>
                <version>1.1.2</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.21</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                    </configuration>
                    <version>3.3</version>
                </plugin>
                <plugin>
                    <!-- 命令:mvn mybatis-generator:generate -->
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-maven-plugin</artifactId>
                    <version>1.3.5</version>
                    <configuration>
                        <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                        <verbose>true</verbose>
                        <overwrite>true</overwrite>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>5.1.38</version>
                        </dependency>
                        <dependency>
                            <groupId>org.mybatis.generator</groupId>
                            <artifactId>mybatis-generator-core</artifactId>
                            <version>1.3.5</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </build>
    </project>
    View Code

    五、web.xml中的shiro filter

      在web系统中,shiro也通过filter进行拦截。filter拦截后将操作权交给spring中配置的filterChain(过虑链儿)shiro提供很多filter。 

      在web.xml中配置filter

        <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <!-- 设置true由servlet容器控制filter的生命周期 -->
            <init-param>
                <param-name>targetFilterLifecycle</param-name>
                <param-value>true</param-value>
            </init-param>
            <!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean -->
            <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>
    View Code

      securityManager:这个属性是必须的。

      loginUrl:没有登录认证的用户请求将跳转到此地址进行认证,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。

      unauthorizedUrl:没有权限默认跳转的页面。

    六、application-shiro.xml

    在applicationContext-shiro.xml 中配置web.xml中fitler对应spring容器中的bean

    <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-4.3.xsd">
    
        <!-- web.xml中shiro的filter对应的bean -->
        <!-- Shiro 的Web过滤器 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager"/>
            <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
            <property name="loginUrl" value="/login.action"/>
            <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
            <property name="successUrl" value="/first.action"/>
            <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面 -->
            <property name="unauthorizedUrl" value="/refuse.jsp"/>
            <!-- 自定义filter配置 -->
            <property name="filters">
                <map>
                    <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
                    <entry key="authc" value-ref="formAuthenticationFilter"/>
                </map>
            </property>
    
            <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
            <property name="filterChainDefinitions">
                <value>
                    <!-- 对静态资源设置匿名访问 -->
                    /images/** = anon
                    /js/** = anon
                    /styles/** = anon
                    <!-- 验证码,可匿名访问 -->
                    /validatecode.jsp = anon
    
                    <!-- 请求 logout.action地址,shiro去清除session -->
                    /logout.action = logout
                    <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
                    <!-- /items/queryItems.action = perms[item:query] /items/editItems.action
                        = perms[item:edit] -->
                    <!-- 配置记住我或认证通过可以访问的地址 -->
                    /index.jsp = user
                    /first.action = user
                    /welcome.jsp = user
                    <!-- /** = authc 所有url都必须认证通过才可以访问 -->
                    /** = authc
                    <!-- /** = anon所有url都可以匿名访问 -->
    
                </value>
            </property>
        </bean>
    
        <!-- securityManager安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realm" ref="customRealm"/>
            <!-- 注入缓存管理器 -->
            <property name="cacheManager" ref="cacheManager"/>
            <!-- 注入session管理器 -->
            <property name="sessionManager" ref="sessionManager"/>
            <!-- 记住我 -->
            <property name="rememberMeManager" ref="rememberMeManager"/>
    
        </bean>
    
        <!-- realm -->
        <bean id="customRealm" class="com.lhx.ssm.shiro.CustomRealm">
            <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
            <property name="credentialsMatcher" ref="credentialsMatcher"/>
        </bean>
    
        <!-- 凭证匹配器 -->
        <bean id="credentialsMatcher"
              class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="md5"/>
            <property name="hashIterations" value="1"/>
        </bean>
    
        <!-- 缓存管理器 -->
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
        </bean>
    
        <!-- 会话管理器 -->
        <bean id="sessionManager"
              class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session的失效时长,单位毫秒 -->
            <property name="globalSessionTimeout" value="600000"/>
            <!-- 删除失效的session -->
            <property name="deleteInvalidSessions" value="true"/>
        </bean>
    
        <!-- 自定义form认证过虑器 -->
        <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
        <bean id="formAuthenticationFilter" class="com.lhx.ssm.shiro.CustomFormAuthenticationFilter">
            <!-- 表单中账号的input名称 -->
            <property name="usernameParam" value="username"/>
            <!-- 表单中密码的input名称 -->
            <property name="passwordParam" value="password"/>
            <!-- 记住我input的名称 -->
            <property name="rememberMeParam" value="rememberMe"/>
        </bean>
    
        <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
            <property name="cookie" ref="rememberMeCookie"/>
        </bean>
        <!-- 记住我cookie -->
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <!-- rememberMe是cookie的名字 -->
            <constructor-arg value="rememberMe"/>
            <!-- 记住我cookie生效时间30天 -->
            <property name="maxAge" value="2592000"/>
        </bean>
    </beans>
    View Code

    七、自定义realm

    此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。

    public class CustomRealm extends AuthorizingRealm {
    
        @Override
        public String getName() {
            return "customRealm";
        }
    
        // 支持什么类型的token
        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof UsernamePasswordToken;
        }
    
        // 认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
    
            // 从token中 获取用户身份信息
            String username = (String) token.getPrincipal();
            // 拿username从数据库中查询
            // ....
            // 如果查询不到则返回null
            if (!username.equals("zhang")) {// 这里模拟查询不到
                return null;
            }
    
            // 获取从数据库查询出来的用户密码
            String password = "123";// 这里使用静态数据模拟。。
            
            // 根据用户id从数据库取出菜单
            //...先用静态数据
            List<SysPermission> menus = new ArrayList<SysPermission>();;
            
            SysPermission sysPermission_1 = new SysPermission();
            sysPermission_1.setName("商品管理");
            sysPermission_1.setUrl("/item/queryItem.action");
            SysPermission sysPermission_2 = new SysPermission();
            sysPermission_2.setName("用户管理");
            sysPermission_2.setUrl("/user/query.action");
            
            menus.add(sysPermission_1);
            menus.add(sysPermission_2);
            
            // 构建用户身份信息
            ActiveUser activeUser = new ActiveUser();
            activeUser.setUserid(username);
            activeUser.setUsername(username);
            activeUser.setUsercode(username);
            activeUser.setMenus(menus);
    
            // 返回认证信息由父类AuthenticatingRealm进行认证
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    activeUser, password, getName());
    
            return simpleAuthenticationInfo;
        }
    
        // 授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            // 获取身份信息
            ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
            //用户id
            String userid = activeUser.getUserid();
            // 根据用户id从数据库中查询权限数据
            // ....这里使用静态数据模拟
            List<String> permissions = new ArrayList<String>();
            permissions.add("item:query");
            permissions.add("item:update");
    
            // 将权限信息封闭为AuthorizationInfo
    
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            for (String permission : permissions) {
                simpleAuthorizationInfo.addStringPermission(permission);
            }
    
            return simpleAuthorizationInfo;
        }
    
    }
    View Code

    八、静态资源

    对静态资源设置逆名访问:

    修改applicationContext-shiro.xml:

                    <!-- 对静态资源设置匿名访问 -->
                    /images/** = anon
                    /js/** = anon
                    /styles/** = anon

    九、登录

    9.1、原理

      使用FormAuthenticationFilter过虑器实现 ,原理如下:

        1、将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl

        2、FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)

        3、FormAuthenticationFilter调用realm传入一个token(username和password)

        4、realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。

          如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)

    9.2、登录页面

      由于FormAuthenticationFilter的用户身份和密码的input的默认值(username和password),修改页面的账号和密码 的input的名称为username和password

    9.3、登录代码的实现  

        // 登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
        @RequestMapping("login")
        public String login(HttpServletRequest request) throws Exception {
            // 如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
            String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
            // 根据shiro返回的异常类路径判断,抛出指定异常信息
            if (exceptionClassName != null) {
                if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                    // 最终会抛给异常处理器
                    throw new CustomException("账号不存在");
                } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
                    throw new CustomException("用户名/密码错误");
                } else if ("randomCodeError".equals(exceptionClassName)) {
                    throw new CustomException("验证码错误 ");
                } else {
                    throw new Exception();// 最终在异常处理器生成未知错误
                }
            }
            // 此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
            // 登陆失败还到login页面
            return "login";
        }
    View Code

    9.4、认证拦截器过滤

    在applicationContext-shiro.xml中配置:

                    <!-- /** = authc 所有url都必须认证通过才可以访问 -->
                    /** = authc

    十、退出

      由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可

      在applicationContext-shiro.xml配置LogoutFilter:

    <!-- 退出拦截,请求logout.action执行退出操作 -->
    /logout.action = logout

    可以删除原来的logout的controller方法代码。

    十一、无权限refuse.jsp

    在applicationContext-shiro.xml配置LogoutFilter:

            <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面 -->
            <property name="unauthorizedUrl" value="/refuse.jsp"/>

    十二、shiro过滤器总结

    过滤器简称

    对应的java类

    anon

    org.apache.shiro.web.filter.authc.AnonymousFilter

    authc

    org.apache.shiro.web.filter.authc.FormAuthenticationFilter

    authcBasic

    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

    perms

    org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

    port

    org.apache.shiro.web.filter.authz.PortFilter

    rest

    org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

    roles

    org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

    ssl

    org.apache.shiro.web.filter.authz.SslFilter

    user

    org.apache.shiro.web.filter.authc.UserFilter

    logout

    org.apache.shiro.web.filter.authc.LogoutFilter

    anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

    authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数

    roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

    perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

    rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

    port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。

    authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

    ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

    user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

    注:

    anon,authcBasic,auchc,user是认证过滤器,

    perms,roles,ssl,rest,port是授权过滤器

  • 相关阅读:
    hdoj_1016Prime Ring Problem
    毛玻璃
    HDOJ1175连连看
    const小结
    指向二维数组的指针
    关于对ACM OJ大数据递归栈溢出问题的解决方案
    Hessian的使用与介绍
    Tomcat调优
    ant使用
    ant使用
  • 原文地址:https://www.cnblogs.com/bjlhx/p/7420665.html
Copyright © 2011-2022 走看看