Spring Boot 整合 Shiro

Spring Boot 整合 Shiro

1. Shiro 简介

  1. Apache Shiro 是一个强大的简单易用的 Java 安全框架,主要用来更便捷的认证、授权、加密、会话管理、与 Web 集成、缓存等;
  2. Shiro 使用起来小而简单;
  3. Shiro 依赖性低,不需要任何框架和容器,Shiro 不仅可以实现 Web 应用的权限管理,还可以实现 C/S 系统,分布式系统权限管理;
  4. Shiro 属于轻量框架。

2. Shiro 核心功能

Spring Boot 整合 Shiro_第1张图片

1. 主要功能

Shiro 主要有三大功能模块:

  1. Subject:主体,代表了当前 “用户”;
  2. SecurityManager:安全管理器,管理所有 Subject,所有与安全有关的操作都会与 SecurityManager 交互,可以配合内部安全组件(类似于 SpringMVC 中的 DispatcherServlet);
  3. Realms:用于进行权限信息的验证,Shiro 从从 Realm 获取安全数据,可以把 Realm 看成 DataSource,即安全数据源,一般需要自己实现。

2. 细分功能

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

3. Spring Boot 整合 Shiro 流程

  1. 导入依赖

    
    <dependency>
        <groupId>org.apache.shirogroupId>
        <artifactId>shiro-spring-boot-web-starterartifactId>
        <version>1.7.0version>
    dependency>
    
  2. 实体类

    使用 Lombok 来简化开发

    /**
     * @author beastars
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private String pwd;
        private String perms;
    }
    
  3. 根据用户名查询数据库

    此处使用 Mybatis-plus 来帮助查询

    使用 Mybatis-plus 需要在启动类上添加@MapperScan("com.lzq.mapper")的注解,来扫描 Mapper 类

    UserMapper:

    /**
     * @author beastars
     */
    public interface UserMapper extends BaseMapper<User> {
    }
    

    UserService:

    /**
     * @author beastars
     */
    @Service
    public class UserService {
        @Autowired
        private UserMapper userMapper;
    
        /**
         * 使用 Mybatis-plus 提供的 QueryWrapper 来进行条件查询
         * 根据用户名查询用户
         */
        public User queryUserByName(String name) {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("name", name);
            return userMapper.selectOne(wrapper);
        }
    }
    
  4. 自定义一个继承了 AuthorizingRealm 的自定义 Realm 用于查询用户的角色和权限信息并保存到权限管理器

    /**
     * 自定义 Realm
     *
     * @author beastars
     */
    public class UserRealm extends AuthorizingRealm {
        @Autowired
        private UserService userService;
    
        // 授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            // 拿到当前登录对象
            Subject subject = SecurityUtils.getSubject();
            User currentUser = (User) subject.getPrincipal();
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 设置当前用户的权限
            info.addStringPermission(currentUser.getPerms());
    
            return info;
        }
        
        // 认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            // 获取 token
            UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
    
            // 从数据库中查询用户
            User user = userService.queryUserByName(userToken.getUsername());
            // 用户名认证
            if (user == null) {
                // 如果不存在
                return null; // 抛出 UnknownAccountException 异常
            }
    
            // 获取当前用户
            Subject subject = SecurityUtils.getSubject();
            // 获取当前用户的 session 信息
            Session session = subject.getSession();
            // 将信息返回前端,实时显示数据
            session.setAttribute("loginUser", user);
    
            // 密码认证,shiro来做,加密
            // 此处第一个参数 user,可以将用户信息传入,就可以使用 SecurityUtils.getSubject() 来获取当前用户的信息了
            return new SimpleAuthenticationInfo(user, user.getPwd(), "");
        }
    }
    
  5. 创建 ShiroConfig,将自定义 Realm、SecurityManager 和 Filter 工厂等注入到 Spring 容器中

    /**
     * @author beastars
     */
    @Configuration
    public class ShiroConfig {
    
        // 1.创建 Realm 对象,需要自定义类,将自己的验证方式加入容器
        @Bean
        public UserRealm userRealm() {
            return new UserRealm();
        }
    
        // 2.DefaultWebSecurityManager,权限管理,配置主要是Realm的管理认证
        @Bean(name = "webSecurityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    
            // 关联 Realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
        // 3.ShiroFilterFactoryBean,Filter工厂,设置对应的过滤条件和跳转条件
        @Bean(name = "shiroFilterFactoryBean")
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(
                @Qualifier("webSecurityManager") DefaultWebSecurityManager securityManager
        ) {
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    
            // 关联 SecurityManager,设置安全管理器
            bean.setSecurityManager(securityManager);
    
            // 添加 Shiro 的内置过滤器
            /*
                anon:无需认证就可以访问
                authc:必须认证了才能访问
                user:必须拥有 记住我 功能才能用
                perms:拥有对某个 资源 的权限才能访问
                role:拥有某个角色权限才能访问
             */
            Map<String, String> filterMap = new LinkedHashMap<>();
    
    //        filterMap.put("/user/update", "anon");
    //        filterMap.put("/user/add", "authc");
    
            // 授权,只有有user:add权限的用户才可以对user资源进行add操作
            filterMap.put("/user/add", "perms[user:add]");
            filterMap.put("/user/update", "perms[user:update]");
    
            // 关联权限
            bean.setFilterChainDefinitionMap(filterMap);
    
            // 设置登录路径
            bean.setLoginUrl("/toLogin");
            // 设置无权访问页面,当访问某个资源无权限时,就跳到这个路径
            bean.setUnauthorizedUrl("/noAuth");
    
            return bean;
        }
    }
    
  6. 设置访问控制器

    /**
     * @author beastars
     */
    @Controller
    public class RouteController {
    
        @RequestMapping("/user/add")
        public String add() {
    
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String update() {
    
            return "user/update";
        }
    
        @RequestMapping("/toLogin")
        public String toLogin() {
            return "login";
        }
    
        @RequestMapping("/login")
        public String login(String username, String password, Model model) {
            // 获取当前用户 Subject
            Subject subject = SecurityUtils.getSubject();
            // 封装用户的登录数据
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
            try {
                // 执行登录方法,如果登录成功没有异常,如果登陆失败抛出异常
                subject.login(token);
                return "index";
            } catch (UnknownAccountException e) {
                model.addAttribute("msg", "用户名不存在");
                return "login";
            } catch (IncorrectCredentialsException e) {
                model.addAttribute("msg", "密码错误");
                return "login";
            }
        }
    
        @RequestMapping("/noAuth")
        @ResponseBody
        public String unauthorized() {
            return "无权限访问";
        }
    }
    

你可能感兴趣的:(spring,boot,shiro)