【狂神说:笔记】安全框架:shiro(入门)

2年前 (2022) 程序员胖胖胖虎阿
399 0 0

目录

  • shiro
    • 1、简介
      • 1.1、什么是shiro
      • 1.2、功能
      • 1.3、shiro架构
    • 2、案例分析
    • 3、动手实践
      • 3.1、自定义Realm配置类
      • 3.2、自定义shiro配置类
      • 3.3、认证和授权

shiro

1、简介

1.1、什么是shiro

  • Apache Shiro 是 Java 的一个安全(权限)框架
  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境

1.2、功能

【狂神说:笔记】安全框架:shiro(入门)

  • Authentication :认证、验证
  • Authorization :授权
  • Session Management :会话管理
  • Crytography :加密机制

1.3、shiro架构

【狂神说:笔记】安全框架:shiro(入门)

Subject

  • 应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject
  • Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等
  • Subject 其实是一个门面,SecurityManager才是实际的执行者
  • 与Subject 的所有交互都会委托给SecurityManager

SecurityManager

  • 安全管理器
  • 所有与安全有关的操作都会与SecurityManager交互,且其管理着所有Subject
  • 它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色

Realm

  • Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法
  • 也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作,可以把Realm 看成DataSource

总结:

  • 应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager
  • 我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断

2、案例分析

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Quickstart {

  private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

  public static void main(String[] args) {
    // 工厂模式,通过shiro.ini 配置文件中的信息,生成一个工厂实例
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);

    // 通过SecurityUtils 获取当前执行的用户Subject:
    Subject currentUser = SecurityUtils.getSubject();

    //通过当前用户拿到Shiro的Session 可以脱离web存值取值
    Session session = currentUser.getSession();
    // 使用session存值取值
    session.setAttribute("someKey", "aValue");
    String value = (String) session.getAttribute("someKey");
    if (value.equals("aValue")) {
      log.info("Retrieved the correct value! [" + value + "]");
    }

    // 让我们登录当前用户,以便我们可以检查角色和权限:
    // 这里和SpringSecurity 使用了类似的代码,判断用户是否被认证
    if (!currentUser.isAuthenticated()) {
      //如果被认证,就可以获得一个令牌(token )
      //通过用户的账号密码生成一个令牌
      UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
      //设置记住我功能
      token.setRememberMe(true);
      try {
        //执行登录操作
        currentUser.login(token);
      } catch (UnknownAccountException uae) {
        //如果用户名不存在
        log.info("There is no user with username of " + token.getPrincipal());
      } catch (IncorrectCredentialsException ice) {
        // 如果密码不正确
        log.info("Password for account " + token.getPrincipal() + " was incorrect!");
      } catch (LockedAccountException lae) {
        //用户被锁定,如密码输出过多,则被锁住
        log.info("The account for username " + token.getPrincipal() + " is locked.  " +"Please contact your administrator to unlock it.");
      }
      // ... 在此处捕获更多异常
      catch (AuthenticationException ae) {
        //意外情况?错误?
      }
    }

    //currentUser些用法
    //打印其标识主体(在这种情况下,为用户名) :
    log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

    // 检查角色是否存在
    if (currentUser.hasRole("schwartz")) {
      log.info("May the Schwartz be with you!");
    } else {
      log.info("Hello, mere mortal.");
    }

    //粗粒度
    if (currentUser.isPermitted("lightsaber:wield")) {
      log.info("You may use a lightsaber ring.  Use it wisely.");
    } else {
      log.info("Sorry, lightsaber rings are for schwartz masters only.");
    }

    //细粒度
    if (currentUser.isPermitted("winnebago:drive:eagle5")) {
      log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'." +"Here are the keys - have fun!");
    } else {
      log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
    }

    //注销
    currentUser.logout();

    //结束
    System.exit(0);
  }
}

主要流程:

//获取当前对象
Subject currentUser = SecurityUtils.getSubject();
//根据当前对象获取对应的Session
Session session = currentUser.getSession();
//判断用户是否被认证,根据用户名和密码生成令牌
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我功能
token.setRememberMe(true);
//拿到令牌进行登录
currentUser.login(token);
//打印其标识主体
currentUser.getPrincipal()
//注销
currentUser.logout();

3、动手实践

3.1、自定义Realm配置类

package com.mtf.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class Realm extends AuthorizingRealm {
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    System.out.println("执行了授权方法!");


    return null;
  }

  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    System.out.println("执行了认证方法!");


    return null;
  }
}

  • 继承AuthorizingRealm 重写里面的“认证”和“授权”方法

3.2、自定义shiro配置类

package com.mtf.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRemal") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
}
  • 执行顺序:UserRealm-》DefaultWebSecurityManager-》ShiroFilterFactoryBean

3.3、认证和授权

请求拦截

//在配置类中添加shiro内置过滤器
//添加shiro内置过滤器
Map<String, String> filterMap = new LinkedHashMap<>();
/*
            anon : 无需认证,就可以访问
            authc : 必须认证,才能访问
            user : 必须拥有 “记住我”功能才能用
            perms : 拥有对某个资源的权限才能访问
            role : 拥有某个角色权限才能访问
        */
filterMap.put("/functions/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

//设置拦截后要跳转的页面,一般是登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");

用户认证(与数据库交互判断登录的用户名账号密码是否正确)

//Controller
//用户认证
@RequestMapping("/login")
public String Login(String username, String password, Model model){
  //获取当前对象
  Subject subject = SecurityUtils.getSubject();

  UsernamePasswordToken token = new UsernamePasswordToken(username,password);
  token.setRememberMe(true);

  try {
    if (!subject.isAuthenticated()){
      subject.login(token);
      return "index";
    }
  } catch (UnknownAccountException e) {
    model.addAttribute("msg","用户名错误!");
    return "login";
  }catch (IncorrectCredentialsException e){
    model.addAttribute("msg","密码错误!");
    return "login";
  }
  return "index";
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  System.out.println("执行了认证方法!");

  UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
  User user = userService.queryByName(token.getUsername());
  if (user==null){
    return null;
  }

  return new SimpleAuthenticationInfo("",user.getPassword(),"");
}

用户授权

//权限设置
filterMap.put("/functions/*","perms[functions:*]");
//通过关键字段来判断用户权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  System.out.println("执行了授权方法!");
  SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

  Subject subject = SecurityUtils.getSubject();
  User user = (User) subject.getPrincipal();
  info.addStringPermission(user.getPerms());
  return info;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
  System.out.println("执行了认证方法!");

  UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
  User user = userService.queryByName(token.getUsername());
  if (user==null){
    return null;
  }

  return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}

shiro和thymeleaf整合

// 整合ShiroDialect:用来整合shiro-thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
  return new ShiroDialect();
}

版权声明:程序员胖胖胖虎阿 发表于 2022年11月6日 下午6:48。
转载请注明:【狂神说:笔记】安全框架:shiro(入门) | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...