基于Spring整合Shiro安全框架

园码生活

共 46279字,需浏览 93分钟

 · 2021-03-25

Shiro安全框架

  • Shiro网址

「http://shiro.apache.org/」

  • Shiro下载地址

「http://www.apache.org/dyn/closer.cgi/shiro/1.5.1/shiro-root-1.5.1-source-release.zip(jdk1.8+  maven3.03+)」

添加相应的依赖

<packaging>pom</packaging>

  <name>Shiro_Dome Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <spring.versions>5.1.0.RELEASE</spring.versions>
    <shiro.version>1.5.1</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>
      <!-- configure logging -->
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.6.1</version>
      </dependency>
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
      </dependency>

      <!-- ehcache-core 依赖 -->
      <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
      <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-ehcache</artifactId>
          <version>1.4.0</version>
      </dependency>
      <!-- spring整合shiro-->
      <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-all</artifactId>
          <version>${shiro.version}</version>
      </dependency>


      <!--  spring和 springmvc 依赖-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.versions}</version>
      </dependency>

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>${spring.versions}</version>
      </dependency>

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>${spring.versions}</version>
      </dependency>

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${spring.versions}</version>
      </dependency>

      <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
      </dependency>

      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>servlet-api</artifactId>
          <version>2.5</version>
      </dependency>


  </dependencies>

  <build>
    <finalName>Shiro_Dome</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
  
   <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>

搭建运行的环境

创建Spring配置文件

<?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">

    <!--    配置SecurityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="realm" ref="jdbcRealm"></property>
        <!--    授权需要去读配置SecurityManager的realms 所以要把realm 编写在securityManager中-->
        <!--        <property name="realms">-->
        <!--            <list>-->
        <!--                <ref bean="jdbcRealm"></ref>-->
        <!--            </list>-->
        <!--        </property>-->

    </bean>

    <!--  配置cacheManager 会起到缓存效果-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!--       创建ehcache.xml文件 -->
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>


    <!--
           配置realm
           配置了自己编写的实现realm的类
    -->
    <bean id="jdbcRealm" class="com.stone.shiro.web.realm.ShiroRealm">
        <!--    通过credentialsMatcher属性进行密码加密操作-->
        <property name="credentialsMatcher">
            <!--    通过HashedCredentialsMatcher来选择加密方法-->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--    hashAlgorithmName指定加密方法-->
                <property name="hashAlgorithmName" value="MD5"></property>
                <!--    hashIterations指定加密次数-->
                <property name="hashIterations" value="2"></property>
            </bean>
        </property>
    </bean>

    <!--
        配置LifecycleBeanPostProcessor 可以自动的来调用配置在spring ioc 容器中 shiro bean的生命周期
    -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!--
         启动ioc 容器中使用shiro的注解 ,并且要再配置了 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>


    <!--
         配置 ShiroFilter
        loginUrl 没有认证时即没有登录时页面
        successUrl 登录成功页面
        unauthorizedUrl 没有权限页面

        注意点 :
        1. ShiroFilterFactoryBean的id必须和web.xml中配置的DelegatingFilterProxy <filter-name>标签值一致


    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/index.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>

        <!--    filterChainDefinitionMap控制shiro框架的访问资源和权限管理-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>

        <!--
            配置哪些页面收保护,并且访问这些页面的权限
            格式 :url = 访问权限
            anon 可以被匿名访问
            authc 必须认证之后才能访问
            logout 退出登录
            采取的第一次优先匹配,及有顺序,编写时需要注意
        -->

        <!-- 配死在xml中-->
        <property name="filterChainDefinitions">
            <value>

                /login.jsp = anon
                /shiro/login = anon
                /shiro/logout = logout

                /user.jsp = roles[user]
                /admin.jsp = roles[admin]

                /** =authc
            </value>
        </property>

    </bean>


    <!--   配置一个LinkedHashMap bean 用来处理资源和权限 代替filterChainDefinitions, 通过实例工厂方式-->
    <bean id="definitionMapBuilder" class="com.stone.shiro.web.factory.FilterChainDefinitionMapBuilder"></bean>
    <!--  实例工厂  -->
    <bean id="filterChainDefinitionMap" factory-bean="definitionMapBuilder"
          factory-method="buildfilterChainDefinitionMap"></bean>

    <!--    添加注解shiro权限bean-->
    <bean class="com.stone.shiro.web.service.ShiroService"></bean>

</beans>

创建SpringMvc配置文件

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"
>
    <!--    mvc扫描包  -->
    <context:component-scan base-package="com.stone.shiro.web"></context:component-scan>

    <!--    mvc视图解析器    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    mvc基础配置  -->
    <mvc:annotation-driven></mvc:annotation-driven>
    <mvc:default-servlet-handler></mvc:default-servlet-handler>

</beans><?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:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"
>
    <!--    mvc扫描包  -->
    <context:component-scan base-package="com.stone.shiro.web"></context:component-scan>

    <!--    mvc视图解析器    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    mvc基础配置  -->
    <mvc:annotation-driven></mvc:annotation-driven>
    <mvc:default-servlet-handler></mvc:default-servlet-handler>

</beans>

日志代码

handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

org.apache.juli.FileHandler.level = FINE
org.apache.juli.FileHandler.directory = ../logs
org.apache.juli.FileHandler.prefix = error-debug.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

创建配置资源和权限所需要的Bean

package com.stone.shiro.web.factory;

import java.util.LinkedHashMap;

/*
    配置资源和权限需要的bean
*/

public class FilterChainDefinitionMapBuilder
{
    public LinkedHashMap<String,Object> buildfilterChainDefinitionMap(){
        //  必须为LinkedHashMap
        LinkedHashMap<String,Object>  map = new LinkedHashMap();
        //  假装查询数据库,从数据库中添加资源和权限
        map.put("/login.jsp","anon");
        map.put("/shiro/login","anon");
        map.put("/shiro/logout","logout");
        map.put("/user.jsp","authc,roles[user]"); //认证并且必须为角色为user
        map.put("/admin.jsp","authc,roles[admin]");//认证并且必须为角色为user
        map.put("/success.jsp","user");//自动登录一样可以进入
        map.put("/**","authc");

        return map;
    }

}

创建认证和授权的类

package com.stone.shiro.web.realm;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashSet;
import java.util.Set;


//  如果只需要认证可以只继承AuthorizingRealm类
public class ShiroRealm extends AuthorizingRealm
{

    //  认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException 
{
        //  在认证时,shiro封装的UsernamePasswordToken会保存到AuthenticationToken中

        //  1.把AuthenticationToken重新转换为UsernamePasswordToken
        UsernamePasswordToken uptoken= (UsernamePasswordToken) token;
        //  2.从UsernamePasswordToken中获取username
        String username = uptoken.getUsername();
        //  3.从数据库中获取数据信息
        System.out.println("模拟从数据库中获取数据信息 username " +username+"所对应的用户信息");
        //  4.用户判断
        if("zs".equals(username)){
            throw  new LockedAccountException("此用户被锁定");
        }else if("aa".equals(username)){
            throw  new UnknownAccountException("用户不存在");
        }
        //  5.根据用户的判断情况,来构建AuthenticationInfo对象并返回 通过情况使用SimpleAuthenticationInfo
        /*
           SimpleAuthenticationInfo参数:
           1.Object principal:认证的实体信息 可以是username
           2.Object credentials:  密码 password
           3.ByteSource credentialssalt :盐值加密最后结果
           4.String realmName:当前realm对象的name,调用父类的方法即可
           6.Object hashedCredentials :计算的盐值


        */

        Object principal = username; // 用户

        //  获取盐值加密密码
        Object credentials= null;
        if("shij".equals(username)){
            credentials = shiroMd5("MD5""123456""shij"2);
        }else if("user".equals(username)){
            credentials= shiroMd5("MD5","123456","user",2);
        }else if("admin".equals(username)){
            credentials= shiroMd5("MD5","123456","admin",2);
        }

//        Object credentials="4280d89a5a03f812751f504cc10ee8a5"; // 加密后密码
        //  当前realm对象的name
        String realmName=getName();
        //  盐值
        ByteSource credentialssalt = ByteSource.Util.bytes(username); //  得到输入账号的盐值
//        SimpleAuthenticationInfo saif=new SimpleAuthenticationInfo(principal,credentials,realmName);
        SimpleAuthenticationInfo saif=new SimpleAuthenticationInfo(principal,credentials,credentialssalt,realmName);

        return saif;
    }

    public static void main(String [] agrs){
        /*
            SimpleHash方法查看加密结果
            String algorithmName:加密的方法
            Object source:需要加密的内容
            Object salt:盐值  ByteSource.Util.bytes 可以获取盐值
            int hashlterations:加密次数
        */

        String algorithmName = "MD5";//4280d89a5a03f812751f504cc10ee8a5
        Object source="123456";
        Object salt= ByteSource.Util.bytes("shij");
        int hashlterations=2;
        Object result=new SimpleHash(algorithmName,source,salt,hashlterations);
        System.out.println(algorithmName+"加密方法,加密"+source+"\t"+hashlterations+"次之后的的结果为:"+result);
    }


    public static Object shiroMd5(String algorithmName, Object source,Object saltValue, int hashlterations){
        Object salt= ByteSource.Util.bytes(saltValue);
        Object result=new SimpleHash(algorithmName,source,salt,hashlterations);
        return result;
    }

    public static Object shiroMd5(String algorithmName, Object source, int hashlterations){
        Object salt = null;
        Object result=new SimpleHash(algorithmName,source,salt,hashlterations);
        return result;
    }


    //  授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
    
{
        //  1.从PrincipalCollection中获取登时信息
        Object primaryPrincipal = principals.getPrimaryPrincipal();
        //  2.利用登陆的用户信息来获取当前登陆用户的角色
        Set<String> roles=new HashSet<>();
        if("admin".equals(primaryPrincipal)){
            //  添加角色
            roles.add("admin");
        }

        if("user".equals(primaryPrincipal) || "shij".equals(primaryPrincipal)){
            //  添加角色
            roles.add("user");
        }

        //  3.创建SimpleAuthorizationInfo,并设置reles属性
        SimpleAuthorizationInfo sainfo=new SimpleAuthorizationInfo(roles);
        //  4.返回SimpleAuthorizationInfo对象
        return sainfo;
    }
}

创建Service层

package com.stone.shiro.web.service;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.session.Session;

public class ShiroService
{
    /*
        @RequiresRoles 必须要有什么角色才能进行访问
    */


    @RequiresRoles({"user"})
    public void getMethod(){
        //  测试shiro session  2. 得到session
        Session session = SecurityUtils.getSubject().getSession();
        Object key = session.getAttribute("key");

        System.out.println("shiro service .... \t  session的值 "+key);
    }

}

创建控制层

package com.stone.shiro.web.controller;

import com.stone.shiro.web.service.ShiroService;
import com.sun.net.httpserver.HttpsServer;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/shiro")
public class LoginController
{
    @Autowired
    private ShiroService shiroService;

    @RequestMapping("/testShiroAnnotation")
    public String testShiroAnnotation(HttpSession session){
        //  测试shiro session  1. 设置session
        session.setAttribute("key","shiroValue");
        shiroService.getMethod();
        return  "ShiroAnnotation";
    }

    @RequestMapping("/login")
    public String shiroLogin(@RequestParam("username") String username, @RequestParam("password") String password){
        //   1.获取shiro框架对外api Subject
        Subject subject = SecurityUtils.getSubject();

        //  2.进行权限认证
        //  2.1isAuthenticated判断用户是否已被认证,即是否登录
        if(!subject.isAuthenticated()){
            // 2.2吧用户名密码封装到UsernamePasswordToken中 shiro框架会吧UsernamePasswordToken封装的内容自动封装到AuthenticatingRealm类的doGetAuthenticationInfo方法的形参中
            UsernamePasswordToken token=new UsernamePasswordToken(username,password);
            //  setRememberMe容许实现自动登录
            token.setRememberMe(true);
            try
            {
                //  2.3执行登录
                subject.login(token);
                return "redirect:/success.jsp";
            }catch (Exception e){
                System.out.println("登录失败 \t" +e.getMessage());
            }


        }

        return "/login";
    }
}

创建登录页面

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020/3/10
  Time: 22:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h3> login page /</h3>

<form action="shiro/login" method="post">
   用户 : <input type="text" name="username"/> <br/>
    密码 :<input type="password" name="password"/> <br/>
    <input type="submit" name="登录"/> <br/>
</form>

</body>
</html>

创建登录成功的页面

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020/3/11
  Time: 15:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h3>登录成功</h3> <br/>
<%--
    principal:登陆用户名
    hasRole角色权限
--%>
欢迎你 :<shiro:principal></shiro:principal> <br/>

<shiro:hasRole name="user">
<a href="user.jsp">user</a>  <br/>
</shiro:hasRole>

<shiro:hasRole name="admin">
<a href="admin.jsp">admin</a> <br/>
</shiro:hasRole>

<a href="shiro/testShiroAnnotation">ShiroAnnotation</a><br/>

<a href="shiro/logout">退出登录</a><br/>
</body>
</html>


   

如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  • 点个【在看】,或者分享转发,让更多的人也能看到这篇内容

  • 关注公众号【园码生活】,不定期分享原创&精品技术文章。

欢迎评论区留下你的精彩评论~          
         

觉得文章不错可以分享到朋友圈让更多的小伙伴看到哦~

客官!在看一下呗          


浏览 12
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报