Shiro

本文详细介绍了Apache Shiro框架在权限和角色认知、认证与授权框架、核心模块以及具体使用方法。通过创建Realm实现登录认证与权限授权,配置Spring整合Shiro,并在Controller中实现登录功能。此外,还探讨了Shiro的过滤器链配置以及权限注解的使用。
摘要由CSDN通过智能技术生成

1、权限、角色的认知

权限:能够做什么,可以操作的功能(能访问的页面,能提交的请求)

一个系统,有很多功能,每个人因为工作职责不同,需要有不同的权限

一个系统就有很多的权限,某些人具有相同的权限,为了便于权限的分配,引入了角色的概念

主管角色:50个权限

给某类人,分配角色即可

认证:登录,对身份的认知


2、认证、权限的框架的认知

三大模块:

1、Subject:用户,每一个登录的用户

2、Security Manager :核心,包含 认证、权限、session管理、加密、记住我、缓存

3、Realm: 登录、权限的访问(数据库)

Security Manager的组成

  1. Authentication:身份认证/登录(账号密码验证)。

  2. Authorization:授权,即角色或者权限验证。

  3. Session Manager:会话管理,用户登录后的session相关管理。

  4. Cryptography:加密,密码加密等。

  5. Web Support:Web支持,集成Web环境。

  6. Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。

  7. Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。

  8. Testing:测试支持;

  9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。

  10. Remember Me:记住我,登录后,下次再来的话不用登录了。

【原理】Shiro的核心原理:过滤器拦截技术


3、 Shiro的使用

1、在SSM框架下使用

2、添加依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
</dependency>

lombok简化实体类的一个框架,使用lombok后,实体类只要写属性即可,无需写构造器、getXXX setXXX方法

【注意】使用lombok除了添加依赖之外,还需要在IDEA中安装lombok插件

file--->settings---->plugins---->搜索Lombok install即可

3、创建表

 

 

 

4、创建实体类

5、编写登录的dao

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.ssm2105.dao.UserDao">
    <select id="login" parameterType="String" resultType="User">
        select id,nickname,pwd,email,userid
        from u_user
        where userid=#{userId}
    </select>
</mapper>

6、编写Realm,在Realm中调用dao

编写认证方法

//认证方法
//登录,获取用户信息
//参数token里面存放了登录的账号以及密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    //判断是否输入账号
    if(StringUtils.isEmpty(token.getPrincipal())){
        //直接返回请求
        return null;
    }
    //获取账号信息
    String userId=token.getCredentials().toString();
    //调用业务逻辑查询用户信息
    User user=userService.login(userId);
    //对查询得到的账户进行判断,如果是null则返回
    if(user==null){
        return null;
    }else{
        //进行密码验证
        SimpleAuthenticationInfo simpleAuthenticationInfo=
                new SimpleAuthenticationInfo(userId,user.getPwd(),getName());
        //在验证密码的过程中,如果密码错误,则直接return null
        //                  如果密码正确,则进行授权,调用 doGetAuthorizationInfo()方法
        return simpleAuthenticationInfo;
    }


}

编写授权方法

先查询用户对应的权限:

 5表联查

SELECT u.id,nickname,pwd,email,userid,r.id rid,r.`name` rname,
       up.id pid,up.`name` pname,up.url
from u_user u
INNER JOIN u_user_role uur on u.id=uur.uid
INNER JOIN u_role r on uur.rid=r.id
INNER JOIN u_role_permission urp on uur.rid=urp.rid
INNER JOIN u_permission up on urp.pid=up.id
where userid='zhangsan'

mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.ssm2105.dao.UserDao">
    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="nickName" column="nickname"/>
        <result property="pwd" column="pwd"/>
        <result property="email" column="email"/>
        <result property="userId" column="userid"/>
        <association property="roleSet" javaType="Role">
            <id property="id" column="rid" ></id>
            <result property="name" column="rname"/>
            <association property="permissionList" javaType="Permission">
                <id property="id" column="pid"/>
                <result property="name" column="pname"/>
                <result property="url" column="url"/>
            </association>
        </association>

    </resultMap>
    <select id="login" parameterType="String" resultMap="userMap">
        SELECT u.id,nickname,pwd,email,userid,r.id rid,r.`name` rname,
               up.id pid,up.`name` pname,up.url
        from u_user u
        INNER JOIN u_user_role uur on u.id=uur.uid
        INNER JOIN u_role r on uur.rid=r.id
        INNER JOIN u_role_permission urp on uur.rid=urp.rid
        INNER JOIN u_permission up on urp.pid=up.id
        where userid=#{userId}
    </select>
</mapper>

授权方法

//授权方法
//查询当前登录用户的角色,以及该角色所对应的权限
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //一个用户有多个角色,每个角色有多个权限
    //1、先获取用户信息
    String userId=(String) principals.getPrimaryPrincipal();
    //2、查询得到用户信息,权限、角色一并得到
    User user=userService.login(userId);
    //添加权限
    //创建权限对象
    SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
    //先获取角色
    for(Role role:user.getRoleSet()){
        //添加角色
        simpleAuthorizationInfo.addRole(role.getName());
        //再根据角色获取权限
        for(Permission permission: role.getPermissionList()){
            //添加获取到的权限
            simpleAuthorizationInfo.addStringPermission(permission.getUrl());
        }
    }

    return simpleAuthorizationInfo;
}

完整的Realm

package com.qf.ssm2105.realms;

import com.qf.ssm2105.pojo.Permission;
import com.qf.ssm2105.pojo.Role;
import com.qf.ssm2105.pojo.User;
import com.qf.ssm2105.service.UserService;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    //授权方法
    //查询当前登录用户的角色,以及该角色所对应的权限
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //一个用户有多个角色,每个角色有多个权限
        //1、先获取用户信息
        String userId=(String) principals.getPrimaryPrincipal();
        //2、查询得到用户信息,权限、角色一并得到
        User user=userService.login(userId);
        //添加权限
        //创建权限对象
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        //先获取角色
        for(Role role:user.getRoleSet()){
            //添加角色
            simpleAuthorizationInfo.addRole(role.getName());
            //再根据角色获取权限
            for(Permission permission: role.getPermissionList()){
                //添加获取到的权限
                simpleAuthorizationInfo.addStringPermission(permission.getUrl());
            }
        }

        return simpleAuthorizationInfo;
    }
    //认证方法
    //登录,获取用户信息
    //参数token里面存放了登录的账号以及密码
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //判断是否输入账号
        if(StringUtils.isEmpty(token.getPrincipal())){
            //直接返回请求
            return null;
        }
        //获取账号信息
        String userId=token.getCredentials().toString();
        //调用业务逻辑查询用户信息
        User user=userService.login(userId);
        //对查询得到的账户进行判断,如果是null则返回
        if(user==null){
            return null;
        }else{
            //进行密码验证
            SimpleAuthenticationInfo simpleAuthenticationInfo=
                    new SimpleAuthenticationInfo(userId,user.getPwd(),getName());
            //在验证密码的过程中,如果密码错误,则直接return null
            //                  如果密码正确,则进行授权,调用 doGetAuthorizationInfo()方法
            return simpleAuthenticationInfo;
        }


    }
}

配置---把Realm和SecurityManager整合在一起

在spring的配置文件中

    <!--整合shrio-->
    <!--创建密码匹配器-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="MD5"></property>
        <property name="hashIterations" value="1024"></property>
    </bean>
    <bean id="customRealm" class="com.qf.ssm2105.realms.CustomRealm">
<!--        <property name="credentialsMatcher" ref="credentialsMatcher"></property>-->
    </bean>
    <!--创建SecurityManager对象-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="customRealm"></property>
    </bean>

<!--配置shiro过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!--进行认证、授权-->
    <property name="securityManager" ref="securityManager"></property>
    <!--对特殊的请求进行配置,相当于放行-->
    <property name="loginUrl" value="/login.html"></property><!--非前后端分离-->
    <!--当没有某个操作的权限时,shiro会抛出异常,只要抛出没有权限的异常,可以跳转到指定的页面-->
    <property name="unauthorizedUrl" value="/unauthorized.html"></property>
    <!--定义过滤规则  过滤器链:配置的顺序就是过滤的顺序-->
    <property name="filterChainDefinitions">
        <value>
            <!--静态资源放行,直接找静态资源,不走:securityManager-->
            /static/** =anon
            /css/** =anon
            /js/** =anon
            /img/** =anon
            /user/register =anon  <!--对用户注册请求放行-->
            /user/login =anon <!--对登录请求放行-->
            <!--退出/登出  shiro清空当前用户的session-->
            /logout =logout  
            <!--user:需要登录才能访问   /** 一定放在最后
                请求都会到达securityManager,进行认证、授权及权限判断
             -->
            /** =user
        </value>
    </property>
</bean>


<!--aop思想进行权限的控制-->
<!--创建shiro的处理器:自动执行AuthorizationAttributeSourceAdvisor类型的增强处理-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!--创建springAop的代理对象,让代理对象执行shiro的处理器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
    depends-on="lifecycleBeanPostProcessor">
    <property name="proxyTargetClass" value="true"></property>
</bean>
<!--创建权限验证的增强处理-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"></property>
</bean>

配置web.xml文件,拦截请求

<!--拦截请求,把请求转移到Shiro的过滤器中-->
<!--
DelegatingFilterProxy对象到bean容器中找名字为shiroFilter的过滤器
把请求都交由名字为shiroFilter的bean进行处理
-->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


4、登录功能的实现

package com.qf.ssm2105.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/user")
public class UserController {
    @GetMapping("/login")
    public String login(String userId,String pwd){
        //Shiro实现登录
        Subject subject= SecurityUtils.getSubject();

        //把账号、密码封装在token里
        UsernamePasswordToken token=new UsernamePasswordToken(userId,pwd);
        try {
            //登录请求
            subject.login(token);
            return "success";
        }catch (UnknownAccountException e){
            return "用户名不存在";
        }catch (AuthenticationException e){
            return "账号或秘密错误";
        }catch (AuthorizationException e){
            return "没有操作权限";
        }
    }
}


5、继续配置

在springMVC的配置文件中,配置权限的增强处理

<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"></property>
</bean>


6、Handler方法中加权限注解

package com.qf.ssm2105.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/dept1")
public class DeptController1 {
    @GetMapping("/add")
    @RequiresPermissions("dept1/add")
    public String add(){
        return "add";
    }
    @GetMapping("/update")
    @RequiresPermissions("dept1/update")
    public String update(){
        return "update";
    }
    @GetMapping("/delete")
    @RequiresPermissions("dept1/delete")
    public String delete(){
        return "delete";
    }
    @GetMapping("/findall")
    @RequiresPermissions("dept1/findall")
    public String findAll(){
        return "findall";
    }
}

【说明】如果没有权限,将抛出异常


7、总结

1、职能:认证、权限验证、记住我、加密、session管理等

2、使用时:5张表存储角色、权限

3、先编写Realm(登录、授权)

4、配置

spring的配置文件

---配置了realm的bean

---配置了securityManager的bean 引用realm的bean

---配置过滤器 anon---无需登录,直接访问(游客)

user---登录后才能访问,认证

authc---登录、授权后,有权限才能访问

---aop的代理实现权限的增强处理

springMVC配置文件

---配置权限的增强处理

web.xml文件

拦截住所有请求,拦截到shiro的过滤器中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值