11

SpringBoot整合Shiro权限框架实战

 3 years ago
source link: https://www.cnblogs.com/chenyanbin/p/shiro.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
SpringBoot整合Shiro权限框架实战 - 陈彦斌 - 博客园

SpringBoot整合Shiro权限框架实战

什么是ACL和RBAC#

ACL#

  • Access Control list:访问控制列表
  • 优点:简单易用,开发便捷
  • 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理
  • 例子:常见的文件系统权限设计,直接给用户加权限

RBAC#

  • Role Based Access Control:基于角色的访问控制
  • 权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限
  • 优点:简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来
  • 缺点:开发比ACL相对复杂
  • 例子:基于RBAC模型的权限验证框架,Apache Shiro

什么是Apache Shiro#

官网地址#

点我直达

介绍#

  Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

什么是身份认证#

  Authentication,身份认证,一般就是登陆校验

什么是授权#

  Authorization,给用户分配角色或者访问某些资源的权限

什么是会话管理#

  Session Management,用户的会话管理员,多数情况下是web session

什么是加密#

  Cryptography,数据加密,比如密码加解密

核心概念#

Subject#

  我们把用户或者程序称为主体,主体去访问系统或者资源

SecurityManager#

  安全管理器,Subject的认证和授权都要在安全管理器下进行

Realm#

  数据域,Shiro和安全数据的连接器,通过realm获取认证授权相关信息

Authenticator#

  认证器,主要负责Subject的认证

Authorizer#

  授权器,主要负责Subject的授权,控制Subject拥有的角色或者权限

Crytography#

  加解密,Shiro的包含易于使用和理解的数据加解密方法,简化了很多复杂的API

Cache Manager#

  缓存管理器,比如认证或授权信息,通过缓存进行管理,提高性能

快速上手#

构建项目#

认证和授权#

QuickStartTest.java

常用API#

        //是否有对应的角色
        subject.hasRole("root");
        //获取subject名
        subject.getPrincipal();
        //检查是否有对应的角色,无返回值,直接在SecurityManager里面进行判断
        subject.checkRole("admin");
        //检查是否有对应的角色
        subject.hasRole("admin");
        //退出登录
        subject.logout();

QuickStartAPITest.java

pom.xml

realm实战#

作用#

  Shiro从Realm获取安全数据

概念#

  • principal:主体的标识,可以有多个,但是需要具有唯一性,如:手机号、邮箱
  • credential:凭证,一般就是密码

内置ini realm#

QuickStartIniTest.java

shiro.ini

校验权限#

QuickStartIniTest.java

shiro.ini

注:配置文件必须ini结尾

内置JdbcRealm#

方式一#

QuickStartJdbcIniTest.java

jdbcrealm.ini

建表语句.sql

注意#

  表名和字段要对应上,否则自定义定,继承:AuthorizingRealm,重写sql查询语句!!!!并重新指定realm类型!!!!

方式二#

QuickStartJdbc2Test.java

自定义realm#

  继承AuthorizingRealm重写授权方法doGetAuthorizationInfo重写认证方法doGetAuthenticationInfo

  UsernamePasswordToken:对应就是shirotoken中有PrincipalCredential

  SimpleAuthorizationInfo:代表用户角色权限信息

  SimpleAuthenticationInfo:代表该用户的认证信息

CustomRealm.java

QuickCustomRealmTest.java

Filter过滤器#

  • 核心过滤器
    • DefaultFilter,配置那个路径对应那个拦截器进行处理
  • authc:org.apache.shiro.web.filter.authc.FormAuthenticationFilter
    • 需要认证登录才能访问
  • user:org.apache.shiro.web.filter.authc.UseerrFilter
    • 用户拦截器,表示必须存在用户
  • anon:org.apache.shiro.web.filter.authc.AnonymoousFilter
    • 匿名拦截器,不需要登录即可访问的资源,匿名用户或游客,一般用于过滤静态资源。
  • roles:org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
    • 角色授权拦截器,验证用户是否拥有角色
    • 参数可写多个,表示某些角色才能通过,多个参数时,写roles["root,role1"],当有多个参数时必须每个参数都通过才算通过
  • perms:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
    • 权限授权拦截器,验证用户是否拥有权限
    • 参数可写多个,表示需要某些权限才能通过,多个参数写perms["user,admin"],当有多个参数时必须每个参数都通过才算可以
  • authcBasci:org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
    • httpBasic,身份验证拦截器
  • logout:org.apache.shiro.web.filter.authc.LogoutFilter
    • 退出拦截器,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(),设置的url
  • port:org.apache.shiro.web.filter.authz.PortFilter
    • 端口拦截器,可通过的端口
  • ssl:org.apache.shiro.web.filter.authz.SslFilter
    • ssl拦截器,只有请求协议是https才能通过

Filter配置路径#

  • 路径通配符支持?、*、**,注意通配符匹配不包含目录分隔符“/”
  • *:可以匹配所有,不加*,可以进行前缀匹配,但多个冒号就需要多个*来匹配
url权限采取第一次匹配优先的方式
?:匹配一个字符,如:/user?,匹配:/user1,但不匹配:/user/
*:匹配零个或多个字符串,如:/add*,匹配:/addtest,但不匹配:/user/1
**:匹配路径中的零个或多个路径,如:/user/**将匹配:/user/xxx/yyy

Shiro权限控制注解#

注解方式#

  • @RequiresRoles(value={"admin","editor"},logical=Logical.AND)
    • 需要角色:admin和editor两个角色,AND表示两个同时成立
  • RequiresPermissions(value={"user:add","user:del"},logical.OR)
    • 需要权限user:add或user:del权限其中一个,OR是或的意思
  • @RequiresAuthentication
    • 已经授过权,调用Subject.isAuthenticated()返回true
  • @RequiresUser
    • 身份验证或通过记住我登录过的

使用文件的方式#

  使用ShiroConfig。

编程方式#

SpringBoot整合Shiro#

技术栈#

  前后端分离+SpringBoot+Mysql+Mybatis+Shiro+Redis+JDK8

数据库表#

shiro_2.sql

项目结构#

CustomRealm.java

CustomSessionManager.java

ShiroConfig.java

AdminController.java

LogoutController.java

OrderController.java

OtherController.java

PublicController.java

VideoController.java

PermissionMapper.java

RoleMapper.java

UserMapper.java

Permission.java

Role.java

RolePermission.java

User.java

UserQuery.java

UserRole.java

CustomException.java

GlobalExceptiions.java

UserServiceImpl.java

UserService.java

JsonData.java

SpringbootShiroApplication.java

application.properties

Md5Test.java

pom.xml

项目源码#

链接: https://pan.baidu.com/s/1adjwICKge83YcPycE8ZaEQ  密码: if9s

项目postman测试#

127.0.0.1:12888/pub/index

127.0.0.1:12888/pub/not_permit

127.0.0.1:12888/pub/need_login

127.0.0.1:12888/pub/login

127.0.0.1:12888/authc/video/play_record

127.0.0.1:12888/admin/video/video_list

127.0.0.1:12888/video/add

127.0.0.1:12888/video/update

备注#

  因为链接较多,就不一一做gif动图了,直接导入项目源码,请求的时候,在header上加入token即可~

Filter过滤器#

业务需求#

  • 一个接口,可以让2个角色中的任意一个访问
  • 自定义一个类,继承:AuthorizationFilter
package com.ybchen.springboot_shiro.config;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Set;

/**
 * @Description:自定义Filter
 * @Author:chenyanbin
 * @Date:2021/1/4 11:14 下午
 * @Versiion:1.0
 */
public class CustomRolesOrAuthorizationFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        //filterChainDefinitionMap.put("/admin/**", "roles[admin,user]"); mappedValue <==> admin,user
        String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        Set<String> roles = CollectionUtils.asSet(rolesArray);
        //当前subject是roles中的任意一个,则有权限访问
        for (String role : roles) {
            if (subject.hasRole(role)) {
                return true;
            }
        }
        return false;
    }
}
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfig ShiroFilterFactoryBean 执行");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //如果访问需要登录的某个接口,却没有登录,则调用此接口(如果不是前后端分离,则跳转页面)
        shiroFilterFactoryBean.setLoginUrl("/pub/need_login");
        //shiroFilterFactoryBean.setLoginUrl("/xxx.jsp");
        //登录成功后,跳转的链接,若前后端分离,没必要设置这个
        //shiroFilterFactoryBean.setSuccessUrl("");
        //登录成功,未授权会调用此方法
        shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit");

        //设置自定义Filter
        Map<String, Filter> filterMap=new LinkedHashMap<>();
        filterMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        //拦截路径,必须使用:LinkedHashMap,要不然拦截效果会时有时无,因为使用的是无序的Map
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //key=正则表达式路径,value=org.apache.shiro.web.filter.mgt.DefaultFilter
        //退出过滤器
        filterChainDefinitionMap.put("/logout", "logout");
        //匿名可以访问,游客模式
        filterChainDefinitionMap.put("/pub/**", "anon");
        //登录用户才可以访问
        filterChainDefinitionMap.put("/authc/**", "authc");
        //管理员角色才能访问
//        filterChainDefinitionMap.put("/admin/**", "roles[admin,user]");
        filterChainDefinitionMap.put("/admin/**", "roleOrFilter[admin,user]");
        //有编辑权限才能访问
        filterChainDefinitionMap.put("/video/update", "perms[video_update]");
        //authc:url必须通过认证才可以访问
        //anon:url可以匿名访问
        //过滤链是顺序执行,从上而下,一般把/**,放到最下面
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

Redis整合CacheManager#

原因#

  授权的时候每次都去查询数据库,对于频繁访问的接口,性能和响应速度比较慢,此处可以使用缓存,提高响应速度,也可以使用Guava(本地内存缓存)。

  Redis(分布式缓存)还不了解的小伙伴,在这里我就不一一讲解了,可以看我以前写过的博客。

添加依赖#

        <!--shiro+redis-->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.3.1</version>
        </dependency>

在ShiroConfig中添加如下代码

//使用自定义cacheManager
    securityManager.setCacheManager(cacheManager());

    /**
     * 配置redisManager
     * @return
     */
    public RedisManager getRedisManager(){
        RedisManager redisManager=new RedisManager();
        redisManager.setHost("127.0.0.1:6379");
        //连接那个数据库
        redisManager.setDatabase(0);
        //设置密码
//        redisManager.setPassword("123");
        return redisManager;
    }

    /**
     * 设置具体cache实现类
     * @return
     */
    public RedisCacheManager cacheManager(){
        RedisCacheManager redisCacheManager=new RedisCacheManager();
        redisCacheManager.setRedisManager(getRedisManager());
        return redisCacheManager;
    }

修改CustomRealm

设置redis缓存过期时间

Redis整合SessionManager#

为啥Session也要持久化#

  重启应用,用户无感知,可以继续以原先的状态继续访问。

修改shiroconfig

ShiroConfig.java

Shiro整合Redis后的源码#

链接: https://pan.baidu.com/s/1cNQfBiw50A-U5izzOQclpw  密码: 6wqt
作者:陈彦斌

个性签名:没有学不会的技术,只有不学习的人!
联系方式:543210188(WeChat/QQ),「推荐WeChat」
posted @ 2021-01-05 23:07  陈彦斌  阅读(277)  评论(5)  编辑  收藏

#1楼

2021-01-06 10:20

Highflyer

做得这么认真,值得鼓励!

#2楼

[楼主] 2021-01-06 10:21

陈彦斌

@Highflyer
/鬼脸

#3楼

2021-01-06 11:44

戎"码"一生

#4楼

2021-01-06 14:31

艺术天分高

#5楼

[楼主] 2021-01-06 14:32

陈彦斌

登录后才能发表评论,立即 登录注册访问 网站首页

Copyright © 2021 陈彦斌
Powered by .NET 5.0 on Kubernetes

& Theme Silence v3.0.0

Wonderful U (Demo Version) - AGA
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : AGA

作曲 : AGA

I never knew (我从未察觉)

When the clock stopped and I'm looking at you (自己在凝视你时如此入神,仿佛时间停止了流逝)

I never thought I'll miss someone like you (我从不知我会如此想念你)

Someone I thought that I knew (想念我曾以为我已了解透彻的你)

I never knew (我从来不认为)

I should have known something wouldn't be true (我应该明白梦想并不总能成真)

Baby you know that I'm so into you (亲爱的 你知道我有多爱你)

More than I know I should do (我本不应如此爱你)

So why why why (所以为什么)

Why should we wait there (我们要等待)

And I I I (而我)

I should be waiting (我应该等待)

Waiting for someone new (等待另一个他进入到我的生命中)

Even though that it wasn't you (即使那个人不是你)

But I know that it's (但我知道)

Wonderful (这一切是无比美好的)

Incredible (它不可思议)

Baby irrational (甚至不合情理)

I never knew it was obsessional (我没有想到这种感觉如此难以抗拒)

And I never knew it was with you oooh (没有想过是你和我一同深陷其中)

Baby if it's just (亲爱的)

Wonderful (如果它是如此美好)

Incredible (不可思议)

Baby irrational (甚至到了荒谬的地步)

I never knew it was so sad (我从未想过这会令我悲伤)

Just so sad (如此悲伤)

I'm so sorry (我很抱歉)

Even now I just cannot feel you feel me ((因为)尽管如此我仍然感受不到你所感受到的我)

Hmmm ((哼唱))

So why why why (所以为什么)

Why should we wait there (我们要等待)

And I I I (而我)

I should be waiting (我应该等待)

Waiting for someone new (等待爱情将我们变得焕然一新)

Even though that it wasn't you (即使那时的你已经不再是现在的你)

But I know that it's (但我知道,这一切)

Wonderful (是无比美好的)

Incredible (它不可思议)

Baby irrational (亲爱的,甚至不合情理)

I never knew it was obsessional (我没有想到这种感觉如此难以抗拒)

And I never knew it was with you oooh (没有想过和我经历这一切的竟是你)

Baby if it's just (亲爱的,如果它是如此)

Wonderful (美好)

Incredible (不可思议)

Baby irrational (甚至不合情理)

I never knew it was so sad (我从不知这会令我感到如此悲伤)

Just so sad (如此悲伤)

I'm so sorry (我很抱歉)

Even now I just cannot feel you fall ((因为)尽管如此我仍然感受不到你所感受到的我)

I don't even know now (我无法确定)

I'm sure you'll wait for me (你是否会为我等待)

Even now I just cannot deny (但即使如此我已无法抗拒)

I just hold on so tight (我只能抱紧你)

Until you and I never could breathe (直到你我都无法呼吸)

Oh (哦..)

Wonderful (美好)

Incredible (不可思议)

Baby irrational (甚至不合情理)

I never knew it was obsessional (我从不知道它如此难以抗拒)

And I never knew it was with you until you tell me to (在你告诉我之前,我不知道和我经历这一切的会是你)

Baby if it's just (如果它真是如此)

Wonderful (美妙)

Incredible (不可思议)

Baby irrational (甚至不合情理)

I never knew it was so sad (我从不知道它会让我感到如此悲伤)

Just so sad (如此悲伤)

I'm so sorry (我很抱歉)

Even now I just cannot feel you feel me ((因为)尽管如此我仍然感受不到你所感受到的我)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK