zoukankan      html  css  js  c++  java
  • springboot(六)-使用shiro

    前提

    写之前纠结了一番,这一节放在shiro里面还是springboot里面。后来想了下,还是放springboot里吧,因为这里没有shiro的新东西,只有springboot添加了新东西的使用。

    但是这里还是基于shiro那边的代码已经内容来写的,对权限和角色做控制。

    pom.xml

    按照我个人习惯,新建一个boot工程,首先当然是先配置一下基本的pom.xml文件啦。后期再开发的时候,需要什么再加。

      1 <?xml version="1.0" encoding="UTF-8"?>
      2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      4     <modelVersion>4.0.0</modelVersion>
      5  
      6   <groupId>com.how2java</groupId>
      7   <artifactId>shiro</artifactId>
      8   <version>0.0.1-SNAPSHOT</version>
      9   <name>shiro</name>
     10   <description>shiro</description>
     11   <packaging>war</packaging>
     12    
     13     <parent>
     14         <groupId>org.springframework.boot</groupId>
     15         <artifactId>spring-boot-starter-parent</artifactId>
     16         <version>1.5.9.RELEASE</version>
     17     </parent>
     18  
     19     <dependencies>
     20         <dependency>
     21             <groupId>org.springframework.boot</groupId>
     22             <artifactId>spring-boot-starter-web</artifactId>
     23         </dependency>
     24         <dependency>
     25             <groupId>org.springframework.boot</groupId>
     26             <artifactId>spring-boot-starter-tomcat</artifactId>
     27              
     28         </dependency>
     29         <dependency>
     30               <groupId>junit</groupId>
     31               <artifactId>junit</artifactId>
     32               <version>3.8.1</version>
     33               <scope>test</scope>
     34         </dependency>
     35         <!-- servlet依赖. -->
     36         <dependency>
     37               <groupId>javax.servlet</groupId>
     38               <artifactId>javax.servlet-api</artifactId>
     39                
     40         </dependency>
     41               <dependency>
     42                      <groupId>javax.servlet</groupId>
     43                      <artifactId>jstl</artifactId>
     44               </dependency>
     45         <!-- tomcat的支持.-->
     46         <dependency>
     47                <groupId>org.apache.tomcat.embed</groupId>
     48                <artifactId>tomcat-embed-jasper</artifactId>
     49                 
     50         </dependency>    
     51         <dependency>
     52             <groupId>org.springframework.boot</groupId>
     53             <artifactId>spring-boot-devtools</artifactId>
     54             <optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
     55         </dependency>
     56          
     57         <!-- mybatis -->
     58         <dependency>
     59             <groupId>org.mybatis.spring.boot</groupId>
     60             <artifactId>mybatis-spring-boot-starter</artifactId>
     61             <version>1.1.1</version>
     62         </dependency>
     63  
     64         <!-- mysql -->
     65         <dependency>
     66             <groupId>mysql</groupId>
     67             <artifactId>mysql-connector-java</artifactId>
     68             <version>5.1.21</version>
     69         </dependency>
     70          
     71         <!-- mybatis 逆向工程 -->
     72         <dependency>
     73           <groupId>org.mybatis.generator</groupId>
     74           <artifactId>mybatis-generator-core</artifactId>
     75           <version>1.3.5</version>
     76         </dependency>
     77          
     78         <!-- shiro -->
     79         <dependency>
     80             <groupId>org.apache.shiro</groupId>
     81             <artifactId>shiro-spring</artifactId>
     82             <version>1.3.2</version>
     83         </dependency>
     84                  
     85         <!-- shiro-web -->
     86         <dependency>
     87             <groupId>org.apache.shiro</groupId>
     88             <artifactId>shiro-web</artifactId>
     89             <version>1.3.2</version>
     90         </dependency>
     91     </dependencies>
     92  
     93     <properties>
     94         <java.version>1.8</java.version>
     95     </properties>
     96  
     97     <build>
     98         <plugins>
     99             <plugin>
    100                 <groupId>org.springframework.boot</groupId>
    101                 <artifactId>spring-boot-maven-plugin</artifactId>
    102             </plugin>
    103         </plugins>
    104     </build>
    105  
    106 </project>
    点击展开

    application.properties

    然后,配置application.properties文件或者application.yml文件,我是喜欢yml风格的。

    server:
      port: 8084
    spring:
      mvc:
        view:
          prefix: /WEB-INF/jsp/
          suffix: .jsp
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8
        username: root
        password: 1234
        driver-class-name: com.mysql.jdbc.Driver

    ShiroConfiguration

    springboot最大的特点是啥呀?没有配置文件呐,但是我们使用别人的工具,人家需要添加配置文件,怎么办?在我看来,这是springboot的一个缺点吧,它居然是写配置类,感觉又回到过去了。因为配置文件的方式可读性要强。而且配置文件的方式就是由配置类发展过来的。嗯嗯嗯~~~~

    ShiroConfiguration 这个类就是shiro配置类,类名怎么命名都可以,就是要用@Configuration 注解表示它是一个配置类

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
    }

    这种写法就和 applicationContext-shiro.xml 中的

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    是同一个作用,只要用@Bean 注解了,就表示是被spring管理起来的对象了。

    这个类里面就声明了SecurityManager,DatabaseRealm, HashedCredentialsMatcher,ShiroFilterFactoryBean 等等东西,只是写法变化了,其实和配置文件里是一样的。
    需要注意一点,URLPathMatchingFilter 并没有用@Bean管理起来。 原因是Shiro的bug, 这个也是过滤器,ShiroFilterFactoryBean 也是过滤器,当他们都出现的时候,默认的什么anno,authc,logout过滤器就失效了。所以不能把他声明为@Bean。

    public URLPathMatchingFilter getURLPathMatchingFilter() {
    return new URLPathMatchingFilter();
    }
      1 package com.how2java.shiro;
      2  
      3 import java.util.HashMap;
      4 import java.util.LinkedHashMap;
      5 import java.util.Map;
      6  
      7 import javax.servlet.Filter;
      8  
      9 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
     10 import org.apache.shiro.mgt.SecurityManager;
     11 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
     12 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
     13 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
     14 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
     15 import org.springframework.context.annotation.Bean;
     16 import org.springframework.context.annotation.Configuration;
     17  
     18 import com.how2java.filter.URLPathMatchingFilter;
     19 import com.how2java.realm.DatabaseRealm;
     20  
     21 @Configuration
     22 public class ShiroConfiguration {
     23     @Bean
     24     public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
     25         return new LifecycleBeanPostProcessor();
     26     }
     27  
     28     /**
     29      * ShiroFilterFactoryBean 处理拦截资源文件问题。
     30      * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
     31      * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     32      *
     33      Filter Chain定义说明
     34      1、一个URL可以配置多个Filter,使用逗号分隔
     35      2、当设置多个过滤器时,全部验证通过,才视为通过
     36      3、部分过滤器可指定参数,如perms,roles
     37      *
     38      */
     39     @Bean
     40     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
     41         System.out.println("ShiroConfiguration.shirFilter()");
     42         ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
     43  
     44         // 必须设置 SecurityManager
     45         shiroFilterFactoryBean.setSecurityManager(securityManager);
     46         // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
     47         shiroFilterFactoryBean.setLoginUrl("/login");
     48         // 登录成功后要跳转的链接
     49         shiroFilterFactoryBean.setSuccessUrl("/index");
     50         //未授权界面;
     51         shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
     52         //拦截器.
     53         Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
     54         //自定义拦截器
     55         Map<String, Filter> customisedFilter = new HashMap<>();
     56         customisedFilter.put("url", getURLPathMatchingFilter());
     57  
     58         //配置映射关系
     59         filterChainDefinitionMap.put("/login", "anon");
     60         filterChainDefinitionMap.put("/index", "anon");
     61         filterChainDefinitionMap.put("/static/**", "anon");
     62         filterChainDefinitionMap.put("/config/**", "anon");
     63         filterChainDefinitionMap.put("/doLogout", "logout");;
     64         filterChainDefinitionMap.put("/**", "url");
     65         shiroFilterFactoryBean.setFilters(customisedFilter);
     66         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
     67         return shiroFilterFactoryBean;
     68     }
     69      
     70     public URLPathMatchingFilter getURLPathMatchingFilter() {
     71         return new URLPathMatchingFilter();
     72     }
     73  
     74     @Bean
     75     public SecurityManager securityManager(){
     76         DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
     77         //设置realm.
     78         securityManager.setRealm(getDatabaseRealm());
     79         return securityManager;
     80     }
     81  
     82     @Bean
     83     public DatabaseRealm getDatabaseRealm(){
     84         DatabaseRealm myShiroRealm = new DatabaseRealm();
     85         myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
     86         return myShiroRealm;
     87     }
     88  
     89     /**
     90      * 凭证匹配器
     91      * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     92      *  所以我们需要修改下doGetAuthenticationInfo中的代码;
     93      * )
     94      * @return
     95      */
     96     @Bean
     97     public HashedCredentialsMatcher hashedCredentialsMatcher(){
     98         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
     99  
    100         hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
    101         hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
    102  
    103         return hashedCredentialsMatcher;
    104     }
    105  
    106     /**
    107      *  开启shiro aop注解支持.
    108      *  使用代理方式;所以需要开启代码支持;
    109      * @param securityManager
    110      * @return
    111      */
    112     @Bean
    113     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
    114         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    115         authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    116         return authorizationAttributeSourceAdvisor;
    117     }
    118 }
    点击展开

    SpringContextUtils.java

    继续上面的知识点,因为URLPathMatchingFilter 没有被声明为@Bean, 那么换句话说 URLPathMatchingFilter 就没有被Spring管理起来,那么也就无法在里面注入 PermissionService类了。 
    但是在业务上URLPathMatchingFilter 里面又必须使用PermissionService类,怎么办呢? 就借助SpringContextUtils 这个工具类,来获取PermissionService的实例。 
    这里提供工具类,下个步骤讲解如何使用这个工具类。

    package com.how2java.util;
     
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
     
    @Component 
    public class SpringContextUtils implements ApplicationContextAware { 
        private static ApplicationContext context; 
       
        public void setApplicationContext(ApplicationContext context) 
                throws BeansException { 
            SpringContextUtils.context = context; 
        } 
       
        public static ApplicationContext getContext(){ 
            return context; 
        } 
    } 

    URLPathMatchingFilter.java

    因为前面两个步骤的原因,既不能把 URLPathMatchingFilter.java 作为@Bean管理起来,又需要在里面使用 PermissionService,所以就用上一步的工具类 SpringContextUtils.java,通过如下方式获取 PermissionService了:

    permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
     
     1 package com.how2java.filter;
     2  
     3 import java.util.Arrays;
     4 import java.util.Set;
     5  
     6 import javax.servlet.ServletRequest;
     7 import javax.servlet.ServletResponse;
     8  
     9 import org.apache.shiro.SecurityUtils;
    10 import org.apache.shiro.authz.UnauthorizedException;
    11 import org.apache.shiro.subject.Subject;
    12 import org.apache.shiro.web.filter.PathMatchingFilter;
    13 import org.apache.shiro.web.util.WebUtils;
    14 import org.springframework.beans.factory.annotation.Autowired;
    15  
    16 import com.how2java.service.PermissionService;
    17 import com.how2java.service.impl.PermissionServiceImpl;
    18 import com.how2java.util.SpringContextUtils;
    19  
    20 public class URLPathMatchingFilter extends PathMatchingFilter {
    21     @Autowired
    22     PermissionService permissionService;
    23  
    24     @Override
    25     protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
    26             throws Exception {
    27         if(null==permissionService)
    28             permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
    29          
    30         String requestURI = getPathWithinApplication(request);
    31         System.out.println("requestURI:" + requestURI);
    32  
    33         Subject subject = SecurityUtils.getSubject();
    34         // 如果没有登录,就跳转到登录页面
    35         if (!subject.isAuthenticated()) {
    36             WebUtils.issueRedirect(request, response, "/login");
    37             return false;
    38         }
    39  
    40         // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
    41         System.out.println("permissionService:"+permissionService);
    42         boolean needInterceptor = permissionService.needInterceptor(requestURI);
    43         if (!needInterceptor) {
    44             return true;
    45         } else {
    46             boolean hasPermission = false;
    47             String userName = subject.getPrincipal().toString();
    48             Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
    49             for (String url : permissionUrls) {
    50                 // 这就表示当前用户有这个权限
    51                 if (url.equals(requestURI)) {
    52                     hasPermission = true;
    53                     break;
    54                 }
    55             }
    56  
    57             if (hasPermission)
    58                 return true;
    59             else {
    60                 UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");
    61  
    62                 subject.getSession().setAttribute("ex", ex);
    63  
    64                 WebUtils.issueRedirect(request, response, "/unauthorized");
    65                 return false;
    66             }
    67  
    68         }
    69  
    70     }
    71 }
    点击展开

    测试

    启动该springboot工程,大家试下效果吧。


    代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/springboot-shiro.zip

  • 相关阅读:
    day 05 讲解java三大特性
    day 02 运算符
    石大 6-9 待提交
    poj分类
    NLog使用总结
    VS 2010下单元测试
    MQTT----物联网常用的消息队列协议
    使用jfreechart生成柱状图、折线图、和饼状图
    JavaBean持久化
    使用maven搭建springMVC开发环境
  • 原文地址:https://www.cnblogs.com/fengyuduke/p/10496299.html
Copyright © 2011-2022 走看看