首页 > 其他分享 >Shiro框架理解:身份验证(Authentication)及授权(Authorization)

Shiro框架理解:身份验证(Authentication)及授权(Authorization)

时间:2024-12-03 15:28:09浏览次数:10  
标签:令牌 resp Authentication 身份验证 token Shiro put filterMap Authorization

目录

 

一、介绍:

执行流程:

1. Shiro的核心组件

2. Shiro 的核心安全管理器 SecurityManager

二、验证(Authentication):

OAuth2Filter extends AuthenticatingFilter:

1. 拦截请求

  2. 验证token是否有效

 executeLogin 方法:

 3. 调用 Realm 进行认证(OAuth2Realm)

三、授权(Authorization)与鉴权:

        1. 在Shiro配置类当中配置权限注解支持:

四、配置过滤器链 


一、介绍:

        shiro框架有身份验证授权会话管理以及加密等功能。

        下面是我对身份验证(Authentication) 和授权(Authorization)功能的解析。如有错误还大家评论区斧正。

执行流程:

 

1. Shiro的核心组件

  1. Subject:代表当前用户,可以是用户、程序或第三方服务。
  2. SecurityManager:整个Shiro框架的核心,用于管理所有安全操作。
  3. Realm:数据源,负责连接实际的数据存储(如数据库、LDAP)以验证和获取用户的权限数据。

2. Shiro 的核心安全管理器 SecurityManager

@Bean("securityManager")
    public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
        // 使用默认的 Web 安全管理器
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自定义的 Realm,用于身份验证和授权
        securityManager.setRealm(oAuth2Realm);
        // 禁用 RememberMe 功能
        securityManager.setRememberMeManager(null);
        return securityManager; // 返回安全管理器
    }

二、验证(Authentication):

        Shiro 框架实现了基于 OAuth2 和 JWT 的身份验证流程,主要包括OAuth2TokenOAuth2RealmOAuth2FilterShiroConfigThreadLocalToken等组件。

OAuth2Filter extends AuthenticatingFilter:

1. 拦截请求

        获取请求中的token,如果token为空则返回null,否则返回一个 OAuth2Token对象。 

        createToken():

@Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String requestToken = getRequestToken(req);
        if (StringUtils.isBlank(requestToken)){
            return null;
        }
        return new OAuth2Token(requestToken);
    }

         OAuth2Token:

public class OAuth2Token implements AuthenticationToken {

    private String token;

    public OAuth2Token(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

  2. 验证token是否有效

         如果token不存在,直接返回401。如果令牌过期,则到Redis当中查看是否存在令牌(这里包含令牌刷新功能,在创建token时,同时将token保存到JWT和Redis当中,并设置Redis中的token过期时间为JWT中的两倍,如果用户在JTW中token未过期时间里登录则会校验JWT中的token,如果超过这个时间就会去Redis当中查找token,如果token存在则刷新令牌,否则需要用户重新进行登录)。

        如果 Token 合法,调用 executeLogin() 方法,将 Token 提交到 Shiro 的安全管理器

        onAccessDenied :

@Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        resp.setHeader("Content-type","text/html;charset=UTF-8");
        //允许跨域请求
        resp.setHeader("Access-Control-Allow-Origin", resp.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Credentials", "true");

        //获取请求token,如果请求里没有token,直接返回401
        String token = getRequestToken(req);
        try {
            jwtUtil.verifyToken(token); //检查令牌是否过期
        }catch (TokenExpiredException e){ //如果令牌过期,检查Redis中是否有令牌,如果存在,就重新生产一个令牌给客户端
            if (redisTemplate.hasKey("token")){ //Redis中有令牌,更新令牌
                redisTemplate.delete(token);//删除Redis令牌
                int userId = jwtUtil.getUserId(token);
                String newToken = jwtUtil.createToken(userId);//生成新的Redis令牌
                redisTemplate.opsForValue().set("token", newToken);//将新令牌保存到Redis中
                threadLocalToken.setToken(newToken);//将新令牌保存到Threadlocal中
            }else {//Redis中没有令牌
                resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                resp.getWriter().print("令牌已过期");
                return false;
            }
        }catch (Exception e){//如果Redis不存在令牌,让用户重新登录
            resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            resp.getWriter().print("无效令牌");
            return false;
        }
        boolean bool = executeLogin(request, response);
        return bool;
    }
 executeLogin 方法:
  •         executeLogin 会尝试创建认证令牌(通过 createToken 方法)并交给 Shiro 的 SecurityManager 进行认证。
  •         如果认证成功,doGetAuthenticationInfo 方法会被调用以完成认证。
  •         如果认证失败,executeLogin 会捕获认证异常,然后触发 onLoginFailure 方法。

         onLoginFailure:

@Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        resp.setHeader("Content-type","text/html;charset=UTF-8");
        //允许跨域请求
        resp.setHeader("Access-Control-Allow-Origin", resp.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Credentials", "true");
        try {
            resp.getWriter().print(e.getMessage());
        } catch (IOException ex) {

        }
        return false;
    }

 3. 调用 Realm 进行认证(OAuth2Realm)

        如果用户信息有效,则返回SimpleAuthenticationInfo对象,包含用户信息和token。返回的认证信息有效,Shiro 将认证视为成功。

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException {
        //从令牌中获取用户userId,检测用户是否被冻结
        String accessToken = (String) Token.getPrincipal();
        int userId = jwtUtil.getUserId(accessToken);
        TbUser tbUser = userService.selectById(userId);
        if (tbUser == null) {
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }
        // 将token信息,用户信息加入到info当中
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(tbUser, accessToken, this.getName());
        return info;
    }

         这里有一个需要注意的点,校验完token后,通过executeLogin()将OAth2Token交给SecurityManager管理,这是SecurityManager会自动调用认证模块(触发绑定到SecurityManager的Realm,也就是OAuth2Realm,并调用其 doGetAuthenticationInfo() 方法)完成具体的认证逻辑。

三、授权(Authorization)与鉴权:

        1. 在Shiro配置类当中配置权限注解支持:

@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        // 设置安全管理器,用于注解支持
        advisor.setSecurityManager(securityManager);
        return advisor; // 返回配置好的 Advisor
    }

        配置好以后,当存在 @RequiresRoles、@RequiresPermissions时,就会对用户的权限进行校验,查看是否有权限使用该方法:

        例:

@RequiresPermissions("user:add")
public void addUser() {
    // ......
}

         SecurityManager交给Realm处理,会自动调用OAuth2Realm中的doGetAuthorizationInfo()方法,获取用户的权限并返回,最终与请求的的权限进行比对。

@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection Collection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //查询用户权限列表
        TbUser user = (TbUser) Collection.getPrimaryPrincipal();
        Set<String> permissions = userService.searchUserPermissions(user.getId());
        //把用户权限加入到info当中
        info.setStringPermissions(permissions);
        return info;
    }

         上面写的是关于后端的鉴权,前端的鉴权可以参考我的另一篇博客 点击前往

四、配置过滤器链 

配置 Shiro 的核心过滤器工厂,用于定义过滤规则
@Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, OAuth2Filter oAuth2Filter) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilter.setSecurityManager(securityManager);

        // 配置自定义的 OAuth2 过滤器
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", oAuth2Filter); // 定义 oauth2 过滤器
        shiroFilter.setFilters(filters);

        // 配置过滤链,定义访问权限
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/app/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/user/register", "anon");
        filterMap.put("/user/login", "anon");
        //filterMap.put("/test/**", "anon");
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter; // 返回配置好的过滤器工厂
    }

         其中配置为“anon”的代表可以匿名访问,“oauth2”的必须经过 oAuth2Filter 处理才能访问。

        具体代码实现可参考点击前往

标签:令牌,resp,Authentication,身份验证,token,Shiro,put,filterMap,Authorization
From: https://blog.csdn.net/qq_67177419/article/details/144205429

相关文章

  • Python_JWT_通常用于身份验证和信息交换
    JWT(JSONWebToken)是一种开放标准(RFC7519),用于在网络应用环境间安全地传输信息。JWT通常用于身份验证和信息交换Jwt由三部分组成,用dian(.)分割Header(头部)Payload(负载)Signature(签名)Header(头部):描述JWT的元数据,通常包括两个部分:typ:指定令牌的类型,通常是JWT。alg:指定......
  • kafka SASL/PLAIN 身份验证
    kafka认证机制使用SSL或SASL对来自客户端(生产者和使用者)、其他代理和工具的代理连接进行身份验证。Kafka支持以下SASL机制:SASL/GSSAPI(Kerberos)-从版本0.9.0.0开始SASL/PLAIN-从版本0.10.0.0开始SASL/SCRAM-SHA-256和SASL/SCRAM-SHA-512-从版本0.1......
  • 身份验证绕过漏洞简析
    在目前可得的描述中可以得出这个漏洞主要是因为使用PKIAuthenticationPlugin的Solr实例(在使用Solr身份验证时默认启用)容易受到身份验证绕过的影响,下来着重分析绕过数据的传递过程前言最近solr爆出了新的身份绕过漏洞,工作中要对该漏洞进行复现,正好将分析的过程记录一......
  • DATAGERRY REST API身份验证绕过漏洞(CVE-2024-46627)
    0X01产品描述:        ‌DATAGERRY是一个灵活的开源CMDB和资产管理工具,它完全将数据模型的定义留给用户。‌用户只需在一个易于使用的webfrontend中定义自己的对象类型(如服务器、路由器、租赁线路、位置等)。通过DATAGERRY的导出API,存储在DATAGERRY中的CMDB对象可以轻......
  • BitLocker加密C盘时:启动时需要附加身份验证
    BitLocker加密C盘时:启动时需要附加身份验证BitLocker加密C盘时显示如下错误信息:此设备无法使用受信任的平台模块。管理员必须在操作系统卷的“启动时需要附加身份验证”策略中设置“没有兼容的TPM时允许BitLocker”选项。BitLocker加密C盘时:启动时需要附加身份验证解决办法:Wind......
  • 关于`django-auth-ldap` 和 `ldap3`如何处理与 LDAP 服务器的连接和身份验证的问题
    在使用Django进行ActiveDirectory(AD)身份验证时,django-auth-ldap和ldap3都涉及到与LDAP服务器的交互。一个关键的差异在于如何处理与LDAP服务器的连接和身份验证。具体来说,django-auth-ldap通常使用一个绑定用户(BindDN)来执行搜索和验证操作,而使用ldap3时,你......
  • MongoDB增加身份验证
    1.数据库添加用户和密码mongo>useadmin>db.createUser({user:"nucRoot",pwd:"f71F!6",roles:["root"]}) 2.修改启动文件,通过auth方式启动,完整如下(INI格式)dbpath=/usr/local/mongodb/data/dblogpath=/usr/local/mongodb/data/logs/mongodb.loglogap......
  • web应用中身份验证与鉴权:Token无感刷新新方案
    有关Web的安全验证与鉴权,Jwt已然成为我们日常开发中最常用的方案,这里的Web既包括各种Web系统和平台,还有各种对外提供的API服务等等。它们都可以用jwt的方案来对客户端请求进行安全验证。但是一般人都知道,token存在一个过期的问题,它会导致我们正在使用系统的过程中,出现突然中断你的......
  • 高级 Python Web 应用中的身份验证与授权机制解析
    高级PythonWeb应用中的身份验证与授权机制解析目录......
  • JWT在分布式架构中的应用实践|使用Java构建安全的身份验证系统|使用Java构建安全的身份
    JWT(JSONWebToken)是一种基于JSON的开放标准,用于在双方之间安全地传输信息。JWT因其轻量级、安全性和跨平台特性,在现代Web应用中被广泛使用。通过JWT,可以方便地进行用户身份验证、信息传递等场景。然而,对于开发者来说,如何正确解析JWT以验证其合法性和提取其中的信息至关重要。在这......