Spring mvc整合shiro 框架详细解析

11 篇文章 0 订阅

一、项目的版本

Spring 全家桶-4.3.5版本

shiro版本-1.3.2 核心容器

下面是maven依赖的配置

<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版本号 -->
  <spring.version>4.3.5.RELEASE</spring.version>
  <spring-data-jpa.version>1.4.4.RELEASE</spring-data-jpa.version>
  <!-- mybatis版本号 -->
  <mybatis.version>3.2.8</mybatis.version>
  <!-- hibernate版本号 -->
  <hibernateVersion>4.3.5.Final</hibernateVersion>

  <!-- mysql驱动版本号 --><!-- 添加mysql驱动依赖 ——在使用5.0+版本数据时使用此驱动-->
  <mysql-driver.version>5.1.29</mysql-driver.version>

  <!-- mysql驱动版本号 -->
  <!--<mysql-driver.version>8.0.12</mysql-driver.version>-->
</properties>
<!-- shiro 核心依赖 -->
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.3.2</version>
</dependency>
  <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.3.2</version>
  </dependency>
  <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.2.2</version>
  </dependency>
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-ehcache</artifactId>
  <version>1.3.2</version>
</dependency>
<!-- 添加spring核心依赖 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</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-oxm</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-jdbc</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-context</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-aop</artifactId>
  <version>${spring.version}</version>
</dependency>

二、配置shiro文件

<?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:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"

        default-lazy-init="true">

    <description>Shiro Configuration</description>

    <!-- 加载配置属性文件 -->
    <!--<context:property-placeholder ignore-unresolvable="true" location="classpath:Shiro.properties" />-->


    <!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <bean id="myRealm" class="com.BBS.Filter.ShiroSecurity.AuthenticationFilter">
        <property name="authorizationCacheName" value="shiroCache"/>
    </bean>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 单一 Realm 应用这样写。如果有多个 Realm ,可以使用 "realms" 属性 -->
        <property name="realm" ref="myRealm"/>
        <!-- 对 Realm 里面的subject进行缓存-->
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 拦截后,发现未登录的跳转到登录页面 -->
        <property name="loginUrl" value="/shiroLogin"/>
        <!-- 登录成功后的页面 -->
        <property name="successUrl" value="bbs_jsp/index"/>
        <!-- 非法访问跳转的页面 -->
        <property name="unauthorizedUrl" value="/403"/>
        <!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 无需认证即可访问的静态资源,还可以添加其他 url -->
                /resource/** = anon
                /images/** = anon


                <!-- 无需认证即可访问的 url -->
                /user/VerifyImg = anon
                /user/login = anon
                /Forum/getForum =anon
                <!--/shiroLogin =anon-->
                /city/bbs =anon
                /city/reg =anon
                /Register= anon
                /getPhoto =anon
                <!-- 除了上述忽略的资源,其他所有资源都需要认证后才能访问 -->
                /** = authc
            </value>
        </property>
    </bean>
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <aop:config proxy-target-class="true"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
    <!-- 支持Shiro对Controller的方法级AOP安全控制 end -->


    <!--Shiro 权限实体缓存 -->
    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:shiroCache.xml"/>
        <!--value="classpath:org/apache/shiro/cache/ehcache/ehcache.xml"-->
        <property name="shared" value="true"/>
    </bean>

    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- 这个属性方法里面有Shiro的自带默认缓存配置,我这里使用的默认配置,可以根据自己的需要进行修改
            具体的可以参看里面的XML文件的参数-->
        <property name="cacheManager"  ref="ehCacheManager">
        </property>

    </bean>
    <!--Shiro 权限实体缓存 -->
</beans>

三、编写shiro权限认证类

这个认证类是跟着上面的配置文件,这个拦截类是可以配置多个,每个应用的地方都可以不一样,我这里配置的主要是拦截用户的总体项目的访问权限模块。大家可以根据自己的需要创建不同的拦截类。

这里面的service类我就不贴出来了,大家可以根据自己的实际service来进行业务操作。当然有需要的可以私下留言

import com.BBS.pojo.User;
import com.BBS.service.GeneralService;
import com.BBS.service.UserService;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author zhanglq
 * @version 1.0
 * @date 2019/11/15 11:34
 * @info shiro权限认证类
 */
public class AuthenticationFilter extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private GeneralService generalService;

    /**
     * shiro授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 使用Shiro提供的方法获取用户名称
        User user = (User) getAvailablePrincipal(principalCollection);

        if (user != null) {
            List list = generalService.findForJdbc("select roleId  from user_role where user_id = ?",new Object[]{user.getId()});
            String roleId = (String) list.get(0);

            // 获取用户的权限
            List roleList = generalService.findForJdbc("select res_id from role_res where role_id = ?", new Object[]{roleId});

            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

            if (roleId != null) {
                info.addRole(roleId);
            }
            if (roleList != null) {
                info.addStringPermission(roleList.toString());
            }
            return info;

        }


        return null;
    }
    /**
     * shiro认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        String loginName = token.getUsername();
        if (loginName != null &&! "".equals(loginName)) {
            //通过登录名获取用户
            User user = userService.findByLoginName(loginName);
            if (user != null) {
                // 如果身份认证验证成功,返回一个AuthenticationInfo实现
                return new SimpleAuthenticationInfo(user.getLoginname(),user.getPassword(),user.getId());
            }
        }
        return null;
    }
}

四、接下是controller 层实现权限的认证。

上面那个类,可以使用注解的形式访问,也可以自己主动访问这个认证类,去查询相关角色用户有没有对应的权限操作。

 @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ModelAndView loginPost(ModelAndView mv, RedirectAttributes redirectAttributes, HttpServletRequest request,User user) {
        String verifyCode = request.getSession().getAttribute("verifyCode").toString().toLowerCase();//获取session中的验证码  不区分大小写

        String vercode = request.getParameter("vercode").toLowerCase();//获取用户提交的验证码
        Subject currentUser = SecurityUtils.getSubject();

        //1. 接受提交的当事人的证书,以及host地址,第一参数用户名,第二个参数密码,第三个参数host,我这里传了ID:
        UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginname(), user.getPassword(), user.getId());
        try {
            //用户认证
            currentUser.login(token);
        } catch (AuthenticationException e) {
            System.out.println(e.getMessage());
            redirectAttributes.addFlashAttribute("message", "用户名或密码错误!");
            mv.setViewName("bbs_jsp/user/Login");
            return mv;
        }
        if (currentUser.isAuthenticated()) {
            //登录成功,保存用户相关信息
            User session_user = this.userService.findByLoginName(user.getLoginname());
            SessionUtils.setAttr(request,AppConstant.SESSION_USER,session_user);
        
            //跳转成功页面
            mv.setViewName("bbs_jsp/index");
            mv.addObject("Forum", page);
            mv.addObject("Weeks", weeks);
            return mv;
        } else {
            redirectAttributes.addFlashAttribute("message", "用户名或密码错误!");
            mv.setViewName("bbs_jsp/user/Login");
            return mv;
        }
    }

上面那里我是主动调用的shiro 的主体类,subject去查询用户的相关权限。subject是核心,每一个用户都可以是一个subject。当然也可以用使用注解去验证

Shiro的认证注解处理是有内定的处理顺序的,如果有个多个注解的话,前面的通过了会继续检查后面的,若不通过则直接返回,处理顺序依次为(与实际声明顺序无关):

当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。

RequiresRoles 

当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresPermissions 

使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证
RequiresAuthentication 

当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
RequiresUser 

使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。
RequiresGuest

上面是的排序是执行的顺序

使用列子

//必须同时属于user和admin角色,可以填写多个
@RequiresRoles({"user","admin"})  

 //符合什么index:hello权限

@RequiresPermissions("index:hello")

下面还需要存放shiro权限的表

/**
 * @author zhanglq
 * @version 1.0
 * @date 2019/11/4 21:47
 * @idfo 角色资源权限表
 */
@Entity
@Table(name = "shiro_resource")
public class ResourceEntity extends commonEntity {


    private String resName;  //资源权限名称

    @Column(name = "res_name",length = 25)
    public String getResName() {
        return resName;
    }

    public void setResName(String resName) {
        this.resName = resName;
    }
}
/**
 * @author zhanglq
 * @version 1.0
 * @date 2019/11/4 22:07
 * @info 用户角色表
 */
@Entity
@Table(name = "role")
public class Role extends commonEntity {


    private String roleName;  // 角色名称


    @Column(name = "role_name",length = 10)
    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}
/**
 * @author zhanglq
 * @version 1.0
 * @date 2019/11/4 21:00
 * @info 角色-资源表,记录每个角色所有的资源权限
 */
@Entity
@Table(name = "role_res")
public class RoleResEntity extends commonEntity {


    private String roleId;   //角色id
    private String resId;   //资源权限ID

    @Column(name = "role_id",length = 5)
    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }
    @Column(name = "res_id",length = 5)
    public String getResId() {
        return resId;
    }

    public void setResId(String resId) {
        this.resId = resId;
    }
}
/**
 * @author zhanglq
 * @version 1.0
 * @date 2019/11/18 11:08
 * @info 用户角色表,记录每个用户所有的角色信息
 */
@Entity
@Table(name = "user_role")
public class UserRole  extends commonEntity {



    private String userId;  // 用户表ID
    private String roleId; //角色表ID

    @Column(name = "user_id",length = 5)
    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
    @Column(name = "role_id",length = 5)
    public String getRoleId() {
        return roleId;
    }

    public void setRoleId(String roleId) {
        this.roleId = roleId;
    }
}

shiro的缓存参数,在shiro配置文件我配置了shiro的缓存,我查看过源码,如果我们配置了缓存类,但是没有配置参数的话。shiro他底层有一个通用参数类型,我这个是通用参数的类型。

<ehcache updateCheck="false" name="shiroCache">

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"/>
</ehcache>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值