自学内容网 自学内容网

Shiro认证

1. Shiro简介

Shiro是一个强大且灵活的Java安全框架,专注于认证、授权、加密、会话管理等功能,能够无缝集成到现有的应用程序中。相比Spring Security,Shiro的学习曲线较为平缓,配置简单

(1) Shiro特性
  • 认证(Authentication):身份验证,确认用户的身份信息
  • 授权(Authorization):基于角色或权限控制访问资源
  • 会话管理(Session Management):支持JavaSE和JavaEE环境下的会话管理
  • 加密(Cryptography):提供用于安全操作的加密API
  • 易扩展性:Shiro的各个模块是解耦的,开发者可以根据需要进行灵活扩展
(2) Shiro架构

Shiro的核心架构主要由以下几部分组成:

  • Subject:表示当前用户,包括已登录和未登录的用户
  • SecurityManager:Shiro的核心控制器,管理所有安全操作,相当于Spring Security中的Filter Chain
  • Realm:用于连接实际数据源(如数据库)和Shiro,负责根据用户凭证获取相应的权限、角色信息
2. 认证(Authentication)

认证是Shiro中的一个重要部分,主要验证用户的身份

(1) Token

Shiro中的认证是基于令牌(Token)的,最常用的令牌是UsernamePasswordToken,它包含用户名和密码,用于在登录时进行身份验证

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.shiro.authc;

public class UsernamePasswordToken implements HostAuthenticationToken, RememberMeAuthenticationToken {
    private String username;
    private char[] password;
    private boolean rememberMe;
    private String host;

    public UsernamePasswordToken() {
        this.rememberMe = false;
    }

    public UsernamePasswordToken(String username, char[] password) {
        this(username, (char[])password, false, (String)null);
    }

    public UsernamePasswordToken(String username, String password) {
        this(username, (char[])(password != null ? password.toCharArray() : null), false, (String)null);
    }

    public UsernamePasswordToken(String username, char[] password, String host) {
        this(username, password, false, host);
    }

    public UsernamePasswordToken(String username, String password, String host) {
        this(username, password != null ? password.toCharArray() : null, false, host);
    }

    public UsernamePasswordToken(String username, char[] password, boolean rememberMe) {
        this(username, (char[])password, rememberMe, (String)null);
    }

    public UsernamePasswordToken(String username, String password, boolean rememberMe) {
        this(username, (char[])(password != null ? password.toCharArray() : null), rememberMe, (String)null);
    }

    public UsernamePasswordToken(String username, char[] password, boolean rememberMe, String host) {
        this.rememberMe = false;
        this.username = username;
        this.password = password;
        this.rememberMe = rememberMe;
        this.host = host;
    }

    public UsernamePasswordToken(String username, String password, boolean rememberMe, String host) {
        this(username, password != null ? password.toCharArray() : null, rememberMe, host);
    }

    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public char[] getPassword() {
        return this.password;
    }

    public void setPassword(char[] password) {
        this.password = password;
    }

    public Object getPrincipal() {
        return this.getUsername();
    }

    public Object getCredentials() {
        return this.getPassword();
    }

    public String getHost() {
        return this.host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public boolean isRememberMe() {
        return this.rememberMe;
    }

    public void setRememberMe(boolean rememberMe) {
        this.rememberMe = rememberMe;
    }

    public void clear() {
        this.username = null;
        this.host = null;
        this.rememberMe = false;
        if (this.password != null) {
            for(int i = 0; i < this.password.length; ++i) {
                this.password[i] = 0;
            }

            this.password = null;
        }

    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.getClass().getName());
        sb.append(" - ");
        sb.append(this.username);
        sb.append(", rememberMe=").append(this.rememberMe);
        if (this.host != null) {
            sb.append(" (").append(this.host).append(")");
        }

        return sb.toString();
    }
}
(2) 认证流程

Shiro的认证流程如下:

  1. 用户提交身份认证请求,通常为登录操作
  2. Shiro将用户信息封装为Token
  3. Subject.login(token)方法调用,将Token传递给SecurityManager
  4. SecurityManager通过Realm查找用户信息并验证凭证
  5. 验证通过后,Shiro会将用户的信息存储到会话中
(3) 记住我VS认证
  • 认证:用户成功登录并创建会话,Session管理用户的登录状态
  • 记住我:用户选择"记住我"功能后,即使关闭浏览器,用户信息也能在下次访问时恢复,但不一定是完全的会话恢复。Shiro通过Cookie机制实现这一点
(4) 注销Logout

注销操作通过Subject.logout()方法实现。Shiro会自动清除Session,并将相关的登录信息从SecurityManager中移除

SecurityUtils.getSubject().logout();//shiro再注销一下
3. SpringBoot+Shiro认证

在SpringBoot项目中集成Shiro非常简单,只需进行少量配置即可实现认证功能

(1) 导入Shiro相关依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.8.0</version>
</dependency>
(2) 自定义Realm

自定义Realm用于连接数据库进行身份认证和授权:

package com.ktjiaoyu.crm.config.shiro;

import com.ktjiaoyu.crm.pojo.User;
import com.ktjiaoyu.crm.service.UserService;
import jakarta.annotation.Resource;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @Author: wangtao
 * @CreateTime: 2024-10-16-19:07
 * @Description: 自定义Realm
 * @Version: 1.0
 */

public class MyShiroRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取权限信息");
        //获取权限信息
        User user = (User) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //暂不授予权限信息,下章再搞授权
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用MyShiroRealm.doGetAuthorizationInfo获取身份信息!");

        //获取身份信息
        UsernamePasswordToken token =  (UsernamePasswordToken) authenticationToken;
        String usrName = token.getUsername();
        User user = userService.getUserByUsrName(usrName);
        if(user == null){
            throw new UnknownAccountException();//账号错误
        }
        if(user.getUsrFlag() == null || user.getUsrFlag().intValue() == 0){
            throw new LockedAccountException();//账号锁定
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUsrPassword(), getName());
        //返回身份信息
        return info;
    }
}
(3) 配置Shiro相关对象

在SpringBoot中配置Shiro,包含SecurityManager、过滤器和自定义Realm

package com.ktjiaoyu.crm.config.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: wangtao
 * @CreateTime: 2024-10-16-19:15
 * @Description: Shiro配置类
 * @Version: 1.0
 */

@Configuration
public class ShiroConfig {

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

    @Bean
    public SecurityManager securityManager(){//安全管理器
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ThreadContext.bind(securityManager);//加上这句代码手动绑定
        //注入Realm
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        //shiro过滤器:权限认证
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //注入securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //权限验证:使用Filter控制资源(URL)的访问
        return shiroFilterFactoryBean;
    }
}
(4) 测试认证登录

配置好之后,可以通过以下方式测试登录功能:

//创建Realm(安全数据源)
IniRealm realm = new IniRealm("classpath:shiro.ini");
//通过shiro.ini配置文件 创建Realm
DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);

//注入创建的Realm(安全数据源)
securityManager.setRealm(realm);
SecurityUtils.setSecurityManager(securityManager);
//进行认证
Subject subject = SecurityUtils.getSubject();
//封装令牌
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
subject.login(token);
}catch (AuthenticationException e){
e.printStackTrace();
}
System.out.println("是否认证通过:"+subject.isAuthenticated());
System.out.println("身份信息:"+subject.getPrincipal());

原文地址:https://blog.csdn.net/Pre_W/article/details/142993768

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