springboot集成shiro

标签: springboot

一 前言

本篇内容是关于shiro权限框架的使用,当然知识追寻者不会手把手傻瓜式的把代码给你,而是先要让你理解权限的核心是什么,shiro进行权限认证的流程,结合核心代码展现方式给大家;

公众号:知识追寻者

知识追寻者(Inheriting the spirit of open source, Spreading technology knowledge;)

二 shio 的认证流程

2.1 认证与授权介绍

首先,需要强调一下,每个权限框架的核心功能就是 认证,与授权,如果读者有着2个概念,学习权限相关的知识将会无比简单;

简单说下认证,认证就是你给服务端一段信息,这段信息服务端收到会跟之前你注册的信息进行比对,比如在无任何加密的情况下将 账号 , 密码 输入 form表单发送请求到服务端,服务端根据你之前注册的账号密码进行比对,如果比对成功,说明认证成功,否则认证失败;复杂一点的认证就是 将你的账号 ,密码进行封装为另一种形式通常称为凭证,签名之类, 再将这种形式凭证的东西发给你,然后每次请求你都带上凭证而后端根据你发送的凭证信息进行解密比对,比对成功就说明认证成功,否则认证失败;

简单说下授权,现在的权限设计基本都是基于 用户 角色 , 权限 方式进行设计, 故一个用户拥有什么样的权限都基于拥有什么样的角色,我们可以给每个角色赋予不同的权限,然后认证成功之后就可以给用户进行授权(也就是将用户拥有的角色,权限返回出去就ok,每次请求的时候附带上权限信息即可,服务端再根据附带的权限信息进行判别);

2.2 shiro的主要概念与结构

  1. Subject 当前主体,你可以理解为当前用户;
  2. SecurityManag: 主体管理器,简单理解为管理着许多用户信息;
  3. Authenticator : 认证,在登陆或者发送亲求的时候进行认证;
  4. Authorizer : 授权,权限控制,赋予用户有哪些用户权限信息;
  5. SessionManager : 会话管理器,
  6. CacheManager : 缓存管理器
  7. Cryptography : 加密,适用于企业级开发,提供算法加密;
  8. Realms : 是shio和应用全安数据之间的连接桥梁;在这边会进行 认证和授权工作;

有关于shiro特色如下,也就是对上面的内容进行分块

更多的介绍信息看官网 : https://www.infoq.com/articles/apache-shiro/

2.3 shiro 认证流程

大致的认证流程如下;其实我们经常使用的没这么复杂, 首先就是 用户发送请求 , 我们拦截请求,在将请求 信息 通过 security manager , 其 内部就到了 Realm 进行 认证 与授权, 最后将请求到达服务端,服务端封装好数据返回给前端; ok 很简单哟;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HULFqgkP-1591101632336)(http://shiro.apache.org/assets/images/ShiroAuthenticationSequence.png)]

三 springboot 整个shiro

这套整合方案 是根据 官方 的步骤整个 将 xml 的 bean 替换为java注解即可 ; 有兴趣的读者看官网

http://shiro.apache.org/spring.html

3.1 pom.xml

本次书写过程,知识追寻者 忽略了建表操作,读者知道 思路如何就很容易实现;

先看下引入的依赖,有需要建表的读者把 >mybatis-spring-boot-starter 注解打开即可;关键是引入shiro核心依赖

 	<dependencies>
        <!-- MyBatis -->
<!--        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>-->
        <!--web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Shiro 核心依赖 -->
        <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.16.18</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.2 shiro配置如下

  • ShiroFilterFactoryBean 是配置拦截器,用户拦截url, anno 表示匿名,任何用户都可以访问; authc表示授权,需要拥有授权信息的用户才可以访问,
  • SecurityManager 是主体管理器,管理许多的主体,并且需要将我们实现的Realm注入其中,进行认证与授权;
  • ShiroRealm 注入了HashedCredentialsMatcher ,也就是产生凭证的地方,其加密方式与需与用户注册时的方式一致,否则会在Realm中认证失败;
  • AuthorizationAttributeSourceAdvisor ,APO支持,主要是需要使用到 @RequiresRoles, @RequiresPermissions注解的时候 才起作用;
  • LifecycleBeanPostProcessor ,Shiro bean的生命周期,看官网的意思是AuthorizationAttributeSourceAdvisor 的实现要基于其才行;
/**
 * @Author lsc
 * <p> </p>
 */
@Configuration
public class ShiroConfiguration {

    // http://shiro.apache.org/spring.html

    // shiro授权拦截
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 创建一个map , 配置拦截信息
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 匿名用户可访问
        filterChainDefinitionMap.put("login/**", "anon");
        filterChainDefinitionMap.put("register/**", "anon");
        // 认证用户可访问
        filterChainDefinitionMap.put("/**", "authc");
        // 登陆url
        shiroFilterFactoryBean.setLoginUrl("/user/unauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    /* *
     * @Author lsc
     * <p>安全管理器,管理多个subject </p>
     * @Param []
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(Realm());
        return securityManager;
    }

    /* *
     * @Author lsc
     * <p>认证信息 </p>
     * @Param []
     */
    @Bean
    public ShiroRealm Realm() {
        // 我们实现的认证授权类
        ShiroRealm realm = new ShiroRealm();
        // 这边配置了加密设置,在用户登陆时创建密码的方式也要一致
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }
    /* *
     * @Author lsc
     * <p> 加密凭证</p>
     * @Param []
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 采用md5加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 循环加密2次
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }
    /* *
     * @Author lsc
     * <p> 开启Shiro-aop注解支持,比如使用到 @RequiresRoles, @RequiresPermissions ,
     * 其依赖于shiro生命周期</p>
     * @Param [securityManager]
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /* *
     * @Author lsc
     * <p> shiro生命周期</p>
     * @Param []
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanProcessor() {

        return new LifecycleBeanPostProcessor();
    }
}

3.3 ShiroRealm

ShiroRealm 实现了 AuthorizingRealm ,其认证AuthenticationInfo方法; 授权AuthorizationInfo 方法, 代码步骤非常完整,不在过多讲解;

/**
 * @Author lsc
 * <p> </p>
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService userService;

    // 简单重写获取授权信息方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取授权信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 获取用户
        SysUserEntity userEntity = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        // 通过用户 - 角色 - 权限的关联 获取  角色 权限 此处省略
        //.................
        // 将角色的权限放入集合
        Set<String> rolesSet = new HashSet<>();
        // 将权限 信息放入集合
        Set<String> permsSet = new HashSet<>();
        // 权限授权
        simpleAuthorizationInfo.setStringPermissions(permsSet);
        // 角色授权
        simpleAuthorizationInfo.setRoles(rolesSet);
        return simpleAuthorizationInfo;
    }

    // shiro获取认证信息,即根据 token 中的用户名从数据库中获取密码、盐等并返回
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取用户名
        String userName = token.getPrincipal().toString();
        // 查询获取加密后的密码
        SysUserEntity user = userService.getUserByUserName(userName);
        String password= user.getPassword();
        // 盐值
        String salt = user.getSalt();
        // 进行验证
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt), getName());
        // 认证通过
        return authenticationInfo;
    }

}

3.4 注册于登陆方法

登陆方法中 SecurityUtils.getSubject(); 获取用户主体信息,将 用户名 和密码 传给 UsernamePasswordToken 调用 subject.login(usernamePasswordToken); 就会到realm中进行认证;

注册方法最重要的就是加密和方式和配置类一致,这边多了给盐值,也就是给密码混淆,使安全性能更加友好,故在realm认证的时候也需要盐值;

 	@Autowired
    SysUserService sysUserService;


    @PostMapping(value = "login")
    public ResponseEntity login(@RequestBody SysUserEntity sysUserEntity) {
        String username = sysUserEntity.getUsername();
        Subject subject = SecurityUtils.getSubject();
        // shiro认证
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, sysUserEntity.getPassword());
        // jwt 集成 shiro认证
        SysUserEntity user = sysUserService.getUserByUserName(username);
        Map map = new HashMap();
        if (user==null){
            System.out.println("用户名不能为空");
            // 抛异常
        }
        try {
            // shiro认证认证
            subject.login(usernamePasswordToken);
            map.put("name",user.getName());

        } catch (AuthenticationException e) {
            System.out.println("认证失败"+e.getMessage());
        }
        return ResponseEntity.ok(map);
    }

    @PostMapping("register")
    public ResponseEntity unauth(@RequestBody SysUserEntity sysUserEntity){
        SysUserEntity userByUserName = sysUserService.getUserByUserName(sysUserEntity.getUsername());
        if (userByUserName!=null){
            System.out.println("用户名不能为空");
            // 抛异常
        }
        // 加密方式
        String hashAlgorithmName = "MD5";
        String credentials = sysUserEntity.getPassword();

        // 加密次数
        int hashIterations = 2;
        // 生成盐,默认长度 16 位
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        ByteSource credentialsSalt = ByteSource.Util.bytes(salt);
        // 加密后的密码
        SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, hashIterations);
        // 设置加密后的密码
        sysUserEntity.setPassword(simpleHash.toString());
        // 设置盐
        sysUserEntity.setSalt(salt);
        // r入库
        sysUserService.addSysUser(sysUserEntity);
        return ResponseEntity.ok(sysUserEntity);
    }
版权声明:本文为youku1327原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/youku1327/article/details/106504474

智能推荐

idea基础–(7)–jsp页面Controller路径自动识别的问题“Cannot resolve controller URL ...”,Cannot resolve variable

idea之所以强大,就是强大的代码提示和联想功能,写起代码来简直不要太爽。但是这几天我发现在我的jsp页面中访问controller路径的时候不会自动提示了,对于这么严谨的我肯定要找出原因啊,哈哈。 最终效果:按住ctrl,同时点击左键会自动跳转到对应的controller代码块,爽。 需要同时满足的条件 JSP页面顶部包含如下代码: 在idea的项目设置中显示如下:  若显示的是spring a...

26_Python基础_继承

面向对象三大特性: 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用, 相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,  产生不同的执行结果,  增加代码的灵活度 1.  单继承 1.1 概念 继承的概念:&...

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

猜你喜欢

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...