自学内容网 自学内容网

Shiro认证 -- (Authentication)

Apache Shiro是一个功能强大的Java安全框架,提供了身份验证(Authentication)、授权(Authorization)、加密(Cryptography)、会话管理(Session Management)、与Web集成、缓存(Caching)等核心安全功能。

Shiro认证的核心概念

  1. Subject:代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫、机器人等。它是一个抽象概念,所有Subject都绑定到SecurityManager。
  2. SecurityManager:安全管理器,Shiro的核心组件,所有与安全有关的操作都会与SecurityManager交互。它管理着所有Subject,并负责进行认证和授权、会话及缓存的管理。
  3. Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限)。SecurityManager要验证用户身份,需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作。可以把Realm看成DataSource,即安全数据源。
  4. Authenticator:认证器,负责主体认证,是一个扩展点。如果用户觉得Shiro默认的不好,可以自定义实现。其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了。

 

Shiro认证的流程:

1. 添加依赖项

        <!--        shiro认证-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>2.0.1</version>
        </dependency>
        <!--  导入shiro标签      -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.1.0</version>

2. 收集用户身份/凭证:如用户名/密码。 配置shiro.ini文件

 3.认证测试

编写测试类


    @Test
    public void testShiro(){
        // 1.创建 Realm(安全数据源)
        IniRealm realm = new IniRealm("classpath:shiro.ini");
        // 2.配置
        DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
        // 创建注入的 Realm(安全数据源)
        securityManager.setRealm(realm);
        SecurityUtils.setSecurityManager(securityManager);
        // 3.操作 Subject , 进行认证
        Subject subject = SecurityUtils.getSubject();
        // 封装一个令牌
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        try {
            subject.login(token); // 登录,即认证
        }catch (AuthenticationException e){
            System.out.println("认证异常:");
            e.printStackTrace();
        }
        System.out.println("是否通过认证: " + subject.isAuthenticated());

        // 认证通过后 进行权限认证
        System.out.println("是否为管理员角色:" + subject.hasRole("管理员"));  // 是否为某角色
        System.out.println("是否能操作用户查看功能:" + subject.isPermitted("user:view")); // 判断是否拥有某权
        subject.checkPermission("user:view");

        System.out.println("身份信息: " + subject.getPrincipal());

        /**
         * 18:43:30.420 [main] INFO org.apache.shiro.session.mgt.AbstractValidatingSessionManager -- Enabling session validation scheduler...
         * 是否通过认证: true
         * 是否为管理员角色:true
         * 是否能操作用户查看功能:true
         * 身份信息: admin
         */
    }

 可以看到我们的一个运行结果 身份认证成功 

 

那如果我们修改密码呢:

  可以看到如果密码和我们的不一样就会 认证异常 !!

  1.  首先通过指定一个ini配置文件来创建一个IniRealm对象;
  2. 接着实例化一个 DefaultSecurityManager,并注入IniRealm 对象;
  3. 再将 DefaultSecurityManager 绑定到 SecurityUtils,这是一个全局设置,设置一次即可通过 SecurityUtils 得到 Subject;
  4. 然后获取身份验证的 Token,如用户名/密码,此处使用UsernamePasswordToken;调用 subject.login 方法进行登录,其会自动委托给 SecurityManager.login 方法进行登录:
  5. 如果身份验证失败请捕获 AuthenticationException或其子类,常见的如:DisabledAccountException(禁用的帐号)、LockedAccountException(锁定的帐号)、UnknownAccountException(错误的帐号)、ExcessiveAttemptsException(登录失败次数过多)、IncorrectCredentialsException(错误的凭证)、ExpiredCredentialsException(过期的凭证)等,具体请查看其继承关系;对于页面的错误消息展示,最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”,防止一些恶意用户非法扫描帐号库;
  6. 如果身份认证成功,后续可以使用subject.isAuthenticated()判断是否认证通过subject.getPrincipal()获得身份信息等。

那么 Shiro认证的流程到低是什么呢?

流程如下:

  1. 首先调用 Subject.login(token)进行登录,其会自动委托给 Security Manager, 调用之前必须通过 SecurityUtils.setSecurityManager()设置Security Manager;
  2. SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证:
  3. Authenticator才是真正的身份验证者,ShiroAPI中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm 身份验证默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行Realm 身份验证;
  5. Authenticator会把相应的token传入Realm,从Realm获取身份信息,如果没有返验证;回抛出异常表示身份验证失败了。此处可以配置多个ealm,将按照相应的顺序及策略进行访问。

 

Shiro 的使用方法:

ShiroConfig :

package com.ty.springbootshiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.ty.springbootshiro.entity.Right;
import com.ty.springbootshiro.service.RoleService;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.metadata.ManagedOperation;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * ShiroConfig
 *
 * @aurhor Administrator  whs
 * @since 2024/10/8
 */
@Configuration
public class ShiroConfig {

    @Resource
    private RoleService roleService;


    /**
     * 开启Shiro注解
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }



    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {  // thymeleaf 页面上使用 shiro 标签
        return new ShiroDialect();
    }

    @Bean
    public MyShiroRealm myShiroRealm() {   // 自定义Realm
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    @Bean
    public SecurityManager securityManager() {  // 安全管理器 SecurityManager
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入bean
        securityManager.setRealm(myShiroRealm());
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { // 过滤器 权限验证
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        // 注入securityManager
        shiroFilterFactory.setSecurityManager(securityManager);

        // 权限验证 : 使用 Filter 控制资源(URL)的访问
        shiroFilterFactory.setLoginUrl("/index");
        shiroFilterFactory.setSuccessUrl("/main");
        shiroFilterFactory.setUnauthorizedUrl("/403"); //没有权限跳转到403

        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); // 必须使用LinkedHashMap 有序集合

        // 配置可以匿名访问资源的url: 静态资源
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/localcss/**", "anon");
        filterChainDefinitionMap.put("/localjs/**", "anon");

        filterChainDefinitionMap.put("/login/**", "anon");
        filterChainDefinitionMap.put("/logout/**", "anon");  // 注销过滤器 , 自动注销

        // 配置需要特定权限才能范文的资源的url
        // 静态授权: 包括全部需要特定权限才能访问的资源URl
        filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
        filterChainDefinitionMap.put("/user/add", "perms[用户添加]");
        filterChainDefinitionMap.put("/user/edit", "perms[用户编辑]");
        filterChainDefinitionMap.put("/user/del", "perms[用户删除]");



        // 动态授权
        List<Right> rights = roleService.findAllRights();
        for (Right right : rights) {
            if (right.getRightUrl()!=null && right.getRightUrl().trim().equals("")) { // .startsWith("/user/")
                filterChainDefinitionMap.put(right.getRightUrl(), "perms["+right.getRightUrl()+"]");
            }
        }


        // 配置认证访问 : 其他资源URl必须认证通过才能访问
        filterChainDefinitionMap.put("/**", "authc");  // 必须放在过滤器链的最后面

        shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactory;
    }


}

indexContriller:

package com.ty.springbootshiro.controller;

import com.alibaba.druid.sql.visitor.functions.Right;
import com.ty.springbootshiro.entity.Role;
import com.ty.springbootshiro.entity.User;
import com.ty.springbootshiro.service.RoleService;
import com.ty.springbootshiro.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * IndexConfig
 *
 * @aurhor Administrator  whs
 * @since 2024/9/13
 */
@Controller
public class IndexController {

    @Resource
    private UserService userService;

    @Resource
    private RoleService roleService;
    /**
     * 去登录页
     */
    @GetMapping("/login")
    public String toLogin() {
        return "login";
    }

    @RequestMapping("/main")
    public String main() {
        return "main";
    }

    @RequestMapping("/403")
    public String unauthorized() {
        return "403";
    }

    @RequestMapping("/login")
    public String login(String usrName, String usrPassword, Model model, HttpSession session) {
//        User loginUser = userService.login(usrName, usrPassword);
//        if (loginUser != null) {
//            session.setAttribute("loginUser", loginUser);
//            return "redirect:/main";
//        }else {
//            model.addAttribute("meg","账号或密码错误");
//            return "login";
//        }

        try {
            UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
            Subject subject = SecurityUtils.getSubject();
            subject.login(token); // 认证登录
            User user = (User) subject.getPrincipal();
            System.out.println("user ------ > " + user);
            session.setAttribute("loginUser", user);

            //获取权限
//            Role role = user.getRole();
//            List<Right> rights = roleService.findRightsByRole(role);
//            role.getRights().addAll(rights);
//            model.addAttribute("rights", rights);
            session.setAttribute("loginUser", user);

            return "redirect:/main";
        }catch (UnknownAccountException | IncorrectCredentialsException e) {
            model.addAttribute("msg", "用户名或密码错误,登录失败!");
            return "login";
        }catch (LockedAccountException e) {
            model.addAttribute("msg", "用户被禁用,登录失败!");
            return "login";
        }catch (AuthenticationException e) {
            model.addAttribute("msg", "认证异常,登录失败!");
            return "login";
        }
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        session.removeAttribute("loginUser");
        SecurityUtils.getSubject().logout(); // shiro 注销
        return "redirect:/main";  //重定向 保证删除Cookie
    }
}

 

ShiroConfig:

package com.ty.springbootshiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.ty.springbootshiro.entity.Right;
import com.ty.springbootshiro.service.RoleService;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.metadata.ManagedOperation;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * ShiroConfig
 *
 * @aurhor Administrator  whs
 * @since 2024/10/8
 */
@Configuration
public class ShiroConfig {

    @Resource
    private RoleService roleService;


    /**
     * 开启Shiro注解
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }



    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {  // thymeleaf 页面上使用 shiro 标签
        return new ShiroDialect();
    }

    @Bean
    public MyShiroRealm myShiroRealm() {   // 自定义Realm
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }

    @Bean
    public SecurityManager securityManager() {  // 安全管理器 SecurityManager
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入bean
        securityManager.setRealm(myShiroRealm());
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { // 过滤器 权限验证
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        // 注入securityManager
        shiroFilterFactory.setSecurityManager(securityManager);

        // 权限验证 : 使用 Filter 控制资源(URL)的访问
        shiroFilterFactory.setLoginUrl("/index");
        shiroFilterFactory.setSuccessUrl("/main");
        shiroFilterFactory.setUnauthorizedUrl("/403"); //没有权限跳转到403

        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); // 必须使用LinkedHashMap 有序集合

        // 配置可以匿名访问资源的url: 静态资源
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/localcss/**", "anon");
        filterChainDefinitionMap.put("/localjs/**", "anon");

        filterChainDefinitionMap.put("/login/**", "anon");
        filterChainDefinitionMap.put("/logout/**", "anon");  // 注销过滤器 , 自动注销

        // 配置需要特定权限才能范文的资源的url
        // 静态授权: 包括全部需要特定权限才能访问的资源URl
        filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
        filterChainDefinitionMap.put("/user/add", "perms[用户添加]");
        filterChainDefinitionMap.put("/user/edit", "perms[用户编辑]");
        filterChainDefinitionMap.put("/user/del", "perms[用户删除]");



        // 动态授权
        List<Right> rights = roleService.findAllRights();
        for (Right right : rights) {
            if (right.getRightUrl()!=null && right.getRightUrl().trim().equals("")) { // .startsWith("/user/")
                filterChainDefinitionMap.put(right.getRightUrl(), "perms["+right.getRightUrl()+"]");
            }
        }


        // 配置认证访问 : 其他资源URl必须认证通过才能访问
        filterChainDefinitionMap.put("/**", "authc");  // 必须放在过滤器链的最后面

        shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactory;
    }


}

 shiro 控制的三种状态:

1.

 

2. 

 

3.

 

 以上便是简单的 shiro 认证 下一章我会讲解什么是shiro 授权


原文地址:https://blog.csdn.net/whs_8792/article/details/142993339

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!