SpringBoot 整合 Shiro 权限框架

Shiro概述

Shiro介绍

Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成:认证授权加密会话管理与 Web 集成缓存 等。借助 Shiro 您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。
在这里插入图片描述

官方网址: https://shiro.apache.org/

基本功能

在这里插入图片描述

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;
  • Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有 信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  • Web Support:Web 支持,可以非常容易的集成到 Web 环境;
  • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率;
  • Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;、
  • Testing:提供测试支持;
  • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

Shiro架构

在这里插入图片描述

  • Subject:任何可以与应用交互的“用户”;
  • SecurityManager :相当于 SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏; 所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进 行认证、授权、会话及缓存的管理。
  • Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证 策略(Authentication Strategy),即什么情况下算用户认证通过了;
  • Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控 制着用户能访问应用中的哪些功能;
  • Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体 的;可以是 JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要 实现自己的 Realm;
  • SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境
  • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
  • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。

SpringBoot整合Shiro

环境搭建

引入依赖

<dependency>
 <groupId>org.apache.shiro</groupId>
 <artifactId>shiro-spring-boot-web-starter</artifactId>
 <version>1.9.0</version>
 </dependency>

指定登录路径

shiro:
 loginUrl: /user/login

创建数据库
user表
在这里插入图片描述
role表
在这里插入图片描述
user_role表
在这里插入图片描述
permissions表
在这里插入图片描述
role_permissions表
在这里插入图片描述

登录、授权、角色认证实现

省略Mybatis查询数据库代码

自定义实现 Realm

@Component("authorizer")
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;


    /**
     * 自定义授权方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1 创建对象,存储当前登录的用户的权限和角色
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //2 获取当前用户身份信息
        String name = principalCollection.getPrimaryPrincipal().toString();
        List<String> roles = userService.getUserRoles(name);
        //3 存储角色
        info.addRoles(roles);
        //4 获取用户角色的权限信息
        List<String> permissions=new ArrayList<>();
        for (String role : roles) {
            List<String> ps = userService.getUserPermission(role);
            permissions.addAll(permissions);
        }
        //5 存储权限信息
        info.addStringPermissions(permissions);
        //6 返回
        return info;
    }

    /**
     * 自定义登录方法
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取用户身份信息
        String name = authenticationToken.getPrincipal().toString();
        //查询用户信息
        User user = userService.getUserByName(name);
        if (user!=null){
            AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    name,//用户名
                    user.getPwd(),//密码
                    ByteSource.Util.bytes("salt"),//加盐字符
                    getName()//realm名称
            );
            return authenticationInfo;
        }
        return null;
    }

}

Shiro配置类

@Configuration
public class ShiroConfig {

    //配置自定义Realm
    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }

    //配置 SecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        //1 创建 defaultWebSecurityManager 对象
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //2 创建加密对象,并设置相关属性
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //2.1 采用 md5 加密
        matcher.setHashAlgorithmName("md5");
        //2.2 迭代加密次数
        matcher.setHashIterations(3);
        //3 将加密对象存储到 myRealm 中
        myRealm().setCredentialsMatcher(matcher);
        //4 将 myRealm 存入 defaultWebSecurityManager 对象
        manager.setRealm(myRealm());
        //5 返回 defaultWebSecurityManager 对象
        return manager;
    }

    //配置 Shiro 内置过滤器拦截范围
    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        //设置不认证可以访问的资源
        definition.addPathDefinition("/user/login","anon");
        //配置退出过滤器
        definition.addPathDefinition("/logout","logout");
        //设置需要进行登录认证的拦截范围
        definition.addPathDefinition("/**","authc");
        return definition;
    }
}

controller代码

@RestController
@RequestMapping("/user")
@ResponseBody
public class UserController {

    @PostMapping(path = "/login",produces = "application/json")
    public String login(@RequestBody User user){
        try {
            //1 获取 Subject 对象
            Subject subject = SecurityUtils.getSubject();
            //2 封装请求数据到 token 对象中
            UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPwd());
            subject.login(token);
            return "登录成功!";
        }
        catch (LockedAccountException e){
            return "账号封禁";

        }
        catch (UnknownAccountException e) {
            e.printStackTrace();
            return  "用户不存在";
        }
        catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            return  "密码错误";
        }
        catch (AuthenticationException e) {
            e.printStackTrace();
            return "登录失败!";
        }
    }

    //登录认证验证角色
    @RequiresRoles("admin")
    @GetMapping("/roles")
    public String userLoginRoles() {
        return "验证角色成功";
    }

    //登录认证验证权限
    @RequiresPermissions("user:delete")
    @GetMapping("/permissions")
    public String userLoginPermissions() {
        return "验证权限成功";
    }
}

权限异常处理

@ControllerAdvice
@ResponseBody
public class PermissionsException {


    @ExceptionHandler(UnauthorizedException.class)
    public String unauthorizedException(Exception ex){
        return "无权限";
    }

}

多个 realm 的认证策略设置

AuthenticationStrategy class描述
AtLeastOneSuccessfulStrategy只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功
FirstSuccessfulStrategy第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略
AllSuccessfulStrategy所有 Realm 成功,认证才视为成功

ModularRealmAuthenticator 内置的认证策略默认实现是 AtLeastOneSuccessfulStrategy 方式。

//配置 SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
 //1 创建 defaultWebSecurityManager 对象
 DefaultWebSecurityManager defaultWebSecurityManager = new 
 DefaultWebSecurityManager();
 //2 创建认证对象,并设置认证策略
 ModularRealmAuthenticator modularRealmAuthenticator = new 
 ModularRealmAuthenticator();
 modularRealmAuthenticator.setAuthenticationStrategy(new 
 AllSuccessfulStrategy());
 
defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
 //3 封装 myRealm 集合
 List<Realm> list = new ArrayList<>();
 list.add(myRealm1);
 list.add(myRealm2);
 //4 将 myRealm 存入 defaultWebSecurityManager 对象
 defaultWebSecurityManager.setRealms(list);
 //5 返回
 return defaultWebSecurityManager;

会话管理

SessionManager由SecurityManager管理。Shiro提供了三种实现:

  • DefaultSessionManager:用于JavaSE环境
  • ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
  • DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的
    会话管理)

获得session方式

Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(“key”,”value”)
  • Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成
    ShiroHttpServletRequest。
  • SecurityManager 和 SessionManager 会话管理器决定 session 来源于ServletRequest还是由 Shiro 管理的会话。无论是通过 request.getSession 或subject.getSession 获取到 session,操作session,两者都是等价的。

学习内容来自尚硅谷

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值