zoukankan      html  css  js  c++  java
  • springboot整合shiro安全框架

    一、shiro简介

    • 概述

      apache shiro 是java的一个轻量级的安全框架,功能没有spring security全面,但对于一般的的项目已经足够。其最大的优点是易于上手,所以大多的公司都会用shiro.

      Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等

    • 组件

    Subject:主体,可以是任何能与应用交互的 “用户”;

    SecurityManager:相当于 SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

    Authenticator:认证器,负责主体认证的, Shiro 提供默认,但也可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

    Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

    Realm:可以有 1 个或多个 Realm,用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

    SessionManager:SessionManager管理Session 的生命周期;Shiro 抽象了一个自己的 Session 来管理主体与应用之间交互的数据;

    SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;

    CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

    Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

    二、使用示例

    1.引入依赖

    <!--shiro-->
    <dependency>
         <groupId>org.apache.shiro</groupId>
         <artifactId>shiro-spring</artifactId>
         <version>1.4.0</version>
    </dependency>
    <!--简化bean-->
    <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
    </dependency>
    
    2.编写实体类
    1. user.java(用户实体类)

      package top.xtslife.security.bean;
      
      import lombok.Data;
      
      import java.io.Serializable;
      import java.util.Set;
      
      /**
       * @Author 小涛
       * @Create 2019-08-20  11:12
       */
      @Data
      public class User implements Serializable {
          private String id;
          private String userName;
          private String password;
          /**
           * 用户对应的角色集合
           */
          private Set<Role> roles;
      
          public User() {
          }
      
          public User(String id, String userName, String password, Set<Role> roles) {
              this.id = id;
              this.userName = userName;
              this.password = password;
              this.roles = roles;
          }
      }
      
    2. Role.java(角色对应实体类)

      package top.xtslife.security.bean;
      
      import lombok.Data;
      
      import java.io.Serializable;
      
      import java.util.Set;
      
      /**
       * @Author 小涛
       * @Create 2019-08-20  11:14
       */
      @Data
      public class Role implements Serializable {
      
          private String id;
          private String roleName;
          /**
           * 角色对应权限集合
           */
          private Set<Permissions> permissions;
      
          public Role() {
          }
      
          public Role(String id, String roleName, Set<Permissions> permissions) {
              this.id = id;
              this.roleName = roleName;
              this.permissions = permissions;
          }
      }
      
    3. Permissions.java(权限对应实体类)

      package top.xtslife.security.bean;
      
      import lombok.Data;
      
      import java.io.Serializable;
      
      /**
       * @Author 小涛
       * @Create 2019-08-20  11:14
       */
      @Data
      public class Permissions implements Serializable {
          private String id;
          private String permissionsName;
      
          public Permissions() {
          }
      
          public Permissions(String id, String permissionsName) {
              this.id = id;
              this.permissionsName = permissionsName;
          }
      }
      
    4. 编写LoginService

      package top.xtslife.security.service;
      
      import top.xtslife.security.bean.User;
      
      /**
       * @Author 小涛
       * @Create 2019/8/20 11:31
       */
      public interface LoginService {
           User getUserByName(String getMapByName);
      }
      
    5. 编写实现类

      package top.xtslife.security.service.impl;
      
      /**
       * @Author 小涛
       * @Create 2019-08-20  11:30
       */
      
      import org.springframework.stereotype.Service;
      import top.xtslife.security.bean.Permissions;
      import top.xtslife.security.bean.Role;
      import top.xtslife.security.bean.User;
      import top.xtslife.security.service.LoginService;
      
      import java.util.HashMap;
      import java.util.HashSet;
      import java.util.Map;
      import java.util.Set;
      
      @Service
      public class LoginServiceImpl implements LoginService {
      
          @Override
          public User getUserByName(String getMapByName) {
              //模拟数据库查询,正常情况此处是从数据库或者缓存查询。
              return getMapByName(getMapByName);
          }
      
          /**
           * 模拟数据库查询
           * @param userName
           * @return
           */
          private User getMapByName(String userName){
              //共添加两个用户,两个用户都是admin一个角色,
              //wsl有query和add权限,zhangsan只有一个query权限
              Permissions permissions1 = new Permissions("1","query");
              Permissions permissions2 = new Permissions("2","add");
              Set<Permissions> permissionsSet = new HashSet<>();
              permissionsSet.add(permissions1);
              permissionsSet.add(permissions2);
              Role role = new Role("1","admin",permissionsSet);
              Set<Role> roleSet = new HashSet<>();
              roleSet.add(role);
              User user = new User("1","wsl","123456",roleSet);
              Map<String ,User> map = new HashMap<>();
              map.put(user.getUserName(), user);
      
              Permissions permissions3 = new Permissions("3","query");
              Set<Permissions> permissionsSet1 = new HashSet<>();
              permissionsSet1.add(permissions3);
              Role role1 = new Role("2","user",permissionsSet1);
              Set<Role> roleSet1 = new HashSet<>();
              roleSet1.add(role1);
              User user1 = new User("2","zhangsan","123456",roleSet1);
              map.put(user1.getUserName(), user1);
              return map.get(userName);
          }
      }
      
    6. 自定义 Realm:用于查询用户的角色和权限信息并保存到权限管理器

      package top.xtslife.security.realm;
      
      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 top.xtslife.security.bean.Permissions;
      import top.xtslife.security.bean.Role;
      import top.xtslife.security.bean.User;
      import top.xtslife.security.service.LoginService;
      
      /**
       * @Author 小涛
       * @Create 2019-08-20  11:45
       */
      public class CustomRealm extends AuthorizingRealm {
      
          @Autowired
          private LoginService loginService;
      
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              //获取登录用户名
              String name = (String) principalCollection.getPrimaryPrincipal();
              //根据用户名去数据库查询用户信息
              User user = loginService.getUserByName(name);
              //添加角色和权限
              SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
              for (Role role : user.getRoles()) {
                  //添加角色
                  simpleAuthorizationInfo.addRole(role.getRoleName());
                  //添加权限
                  for (Permissions permissions : role.getPermissions()) {
                      simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
                  }
              }
              return simpleAuthorizationInfo;
          }
      
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
              //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
              if (authenticationToken.getPrincipal() == null) {
                  return null;
              }
              //获取用户信息
              String name = authenticationToken.getPrincipal().toString();
              User user = loginService.getUserByName(name);
              if (user == null) {
                  //这里返回后会报出对应异常
                  return null;
              } else {
                  //这里验证authenticationToken和simpleAuthenticationInfo的信息
                  SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
                  return simpleAuthenticationInfo;
              }
          }
      }
      
    7. 写javaconfig:把 CustomRealm 和 SecurityManager 等加入到 spring 容器
      package top.xtslife.security.config;
      
      import org.apache.shiro.mgt.SecurityManager;
      import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
      import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
      import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
      import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
      import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import top.xtslife.security.realm.CustomRealm;
      
      import java.util.HashMap;
      import java.util.Map;
      
      @Configuration
      public class ShiroConfig {
          //不加这个注解不生效,具体不详
          @Bean
          @ConditionalOnMissingBean
          public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
              DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
              defaultAAP.setProxyTargetClass(true);
              return defaultAAP;
          }
      
          //将自己的验证方式加入容器
          @Bean
          public CustomRealm myShiroRealm() {
              CustomRealm customRealm = new CustomRealm();
              return customRealm;
          }
      
          //权限管理,配置主要是Realm的管理认证
          @Bean
          public SecurityManager securityManager() {
              DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
              securityManager.setRealm(myShiroRealm());
              return securityManager;
          }
      
          //Filter工厂,设置对应的过滤条件和跳转条件
          @Bean
          public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
              ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
              shiroFilterFactoryBean.setSecurityManager(securityManager);
              Map<String, String> map = new HashMap<>();
              //登出
              map.put("/logout", "logout");
              //对所有用户认证
              map.put("/**", "authc");
              //登录
              shiroFilterFactoryBean.setLoginUrl("/login");
              //首页
              shiroFilterFactoryBean.setSuccessUrl("/index");
              //错误页面,认证不通过跳转
              shiroFilterFactoryBean.setUnauthorizedUrl("/error");
              shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
              return shiroFilterFactoryBean;
          }
      
          //加入注解的使用,不加入这个注解不生效
          @Bean
          public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
              AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
              authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
              return authorizationAttributeSourceAdvisor;
          }
      }
      
    8. 编写controller

      package top.xtslife.security.controller;
      
      import io.swagger.annotations.Api;
      import io.swagger.annotations.ApiOperation;
      import org.apache.shiro.SecurityUtils;
      import org.apache.shiro.authc.AuthenticationException;
      import org.apache.shiro.authc.UsernamePasswordToken;
      import org.apache.shiro.authz.AuthorizationException;
      import org.apache.shiro.authz.annotation.RequiresPermissions;
      import org.apache.shiro.authz.annotation.RequiresRoles;
      import org.apache.shiro.subject.Subject;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      import top.xtslife.security.bean.User;
      
      @RestController
      @Api(tags ="LoginController",description = "用户登录管理")
      public class LoginController {
      
          @RequestMapping("/login")
          @ApiOperation("用户登录")
          public String login(User user) {
              //添加用户认证信息
              Subject subject = SecurityUtils.getSubject();
              UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                      user.getUserName(),
                      user.getPassword()
              );
              try {
                  //进行验证,这里可以捕获异常,然后返回对应信息
                  subject.login(usernamePasswordToken);
      //            subject.checkRole("admin");
      //            subject.checkPermissions("query", "add");
              } catch (AuthenticationException e) {
                  e.printStackTrace();
                  return "账号或密码错误!";
              } catch (AuthorizationException e) {
                  e.printStackTrace();
                  return "没有权限";
              }
              return "login success";
          }
          //注解验角色和权限
          @RequiresRoles("admin")
          @RequiresPermissions("add")
          @RequestMapping("/index")
          @ApiOperation("通过验证访问主页面")
          public String index() {
              return "index!";
          }
      }
      
    9. 编写异常处理

      package top.xtslife.security.component;
      
      import lombok.extern.slf4j.Slf4j;
      import org.apache.shiro.authz.AuthorizationException;
      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.ResponseBody;
      
      @ControllerAdvice
      @Slf4j
      public class MyExceptionHandler {
      
          @ExceptionHandler
          @ResponseBody
          public String ErrorHandler(AuthorizationException e) {
              log.error("没有通过权限验证!", e);
              return "没有通过权限验证!";
          }
      }
      


    作者:关小涛
    学习和分享是博客最大的乐趣,欢迎大家取之所需。
    努力是自己努力的原因,每周天写博客总结工作中的新技能和出现的问题
  • 相关阅读:
    Oracle Hint的用法
    利用flashback transaction query新特性进行事务撤销
    存储的一些基本概念(HBA,LUN)
    SAN和NAS
    SAN (Storage Attached Network),即存储区域网络
    深入浅出谈存储之NAS是什么
    对于NAS,IP SAN以及iSCSCI SAN存储的一些认识和理解
    Oracle的体系结构
    利用360免费wifi搭建局域网让他人访问Oracle数据库
    杭电ACM id:3783
  • 原文地址:https://www.cnblogs.com/XtsLife/p/11383227.html
Copyright © 2011-2022 走看看