当前位置: 首页 > news >正文

Shiro认证(Authentication)

Shiro简介:特性和架构

Apache Shiro是一个功能强大且易于使用的Java安全(权限)框架,提供了认证、授权、会话管理、加密、与Web集成、缓存等功能。Shiro不仅可以在JavaSE环境中使用,也可以在JavaEE环境中使用。

特性

  1. Authentication(认证):身份认证/登录,验证用户是否拥有相应的身份。
  2. Authorization(授权):权限验证,验证某个已认证的用户是否拥有某个权限。
  3. Session Management(会话管理):管理用户登录后的会话信息。
  4. Cryptography(加密):保护数据的安全性,如密码加密存储到数据库。
  5. Web Support:非常容易集成到Web环境。
  6. Caching:缓存用户信息、角色和权限,提高效率。
  7. Concurrency:支持多线程应用的并发验证。
  8. Remember Me:记住我功能,一次登录后下次无需再次登录。

架构

Shiro的架构从外部和内部来看可以分为两个部分:

  1. 外部架构

    • Subject:代表当前“用户”,与应用交互的任何东西都可以是Subject,如网络爬虫、机器人等。所有Subject都绑定到SecurityManager。
    • SecurityManager:安全管理器,所有与安全有关的操作都会与SecurityManager交互,且它管理着所有Subject。它是Shiro的核心,负责与Shiro的其他组件进行交互。
    • Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限)。SecurityManager要验证用户身份,需要从Realm获取相应的用户进行比较以确定用户身份是否合法。
  2. 内部架构

    • Authenticator:认证器,负责主体认证。
    • Authorizer:授权器,决定主体是否有权限进行相应的操作。
    • SessionManager:管理会话的生存周期。
    • SessionDAO:数据访问对象,用于会话的CRUD操作。
    • CacheManager:缓存控制器,管理用户、角色、权限等的缓存。
    • Cryptography:密码模块,提供常见的加密组件用于加密/解密操作。

认证

Token

在Shiro中,认证指的是识别和证明操作者是一个合法用户。用户如果想要通过认证,需要提供Principal(身份)和Credentials(凭证),从而应用能验证用户身份。这些身份和凭证信息在Shiro框架中以Token(令牌)的概念进行封装。

快速上手

添加依赖

<dependency>  <groupId>org.apache.shiro</groupId>  <artifactId>shiro-spring</artifactId>  <version>2.0.1</version>  
</dependency>  
<dependency>  <groupId>javax.servlet</groupId>  <artifactId>javax.servlet-api</artifactId>  <version>4.0.1</version>  <scope>provided</scope>  
</dependency>

配置shiro.ini:在resource目录下创建shiro.ini文件配置用户认证数据。

# 对用户信息进行匹配  
[users]  
# 用户账号和密码  
admin=123456  
czkt=111111

认证测试

public class ShiroTester {  @Test  public void testShiro() {  IniRealm realm = new IniRealm("classpath:shiro.ini");  DefaultSecurityManager securityManager = new DefaultSecurityManager();  securityManager.setRealm(realm);  SecurityUtils.setSecurityManager(securityManager);  Subject subject = SecurityUtils.getSubject();  UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");  try {  subject.login(token);  } catch (Exception e) {  System.out.println("认证异常:");  e.printStackTrace();  }  System.out.println("是否认证成功:" + subject.isAuthenticated());  System.out.println("身份信息:" + subject.getPrincipal());  }  
}


认证流程

  1. 调用Subject.login(Token)进行登录,其会自动委托给SecurityManager
  2. SecurityManager负责真正的身份验证逻辑,它会委托给Authenticator进行身份验证。
  3. Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证。
  4. Authenticator会把相应的Token传入Realm,从Realm获取身份信息,如果没有返回或抛出异常表示身份验证失败。

记住我 vs 认证

“记住我”功能允许用户在一次登录后,下次访问时无需再次登录。这与普通的认证流程有所不同,因为“记住我”功能通常会在用户的浏览器中存储一个持久化的Cookie,用于在用户下次访问时自动进行身份验证。

注销Logout

用户可以通过调用Subject.logout()方法进行注销操作。这会委托给SecurityManager进行实际的注销逻辑处理。

SpringBoot + Shiro 认证

1. 基础代码调整

首先,你需要在Spring Boot项目中添加Shiro的依赖。这可以通过在pom.xml文件中添加以下依赖来完成:

<dependency>  <groupId>org.apache.shiro</groupId>  <artifactId>shiro-spring-boot-starter</artifactId>  <version>1.7.1</version> <!-- 请根据需要选择最新版本 -->  
</dependency>

2. 自定义Realm

Realm是Shiro与数据源之间的桥梁,用于获取安全数据(如用户、角色和权限)。你需要创建一个自定义的Realm类,并实现AuthorizingRealm接口。

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;  
import org.springframework.beans.factory.annotation.Autowired;  // 假设你有一个UserService用于处理用户相关的业务逻辑  
@Component  
public class CustomRealm extends AuthorizingRealm {  @Autowired  private UserService userService;  @Override  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  String username = (String) principals.getPrimaryPrincipal();  // 从数据库中获取用户的角色和权限信息  Set<String> roles = userService.getRolesByUsername(username);  Set<String> permissions = userService.getPermissionsByUsername(username);  SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  authorizationInfo.setRoles(roles);  authorizationInfo.setStringPermissions(permissions);  return authorizationInfo;  }  @Override  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  UsernamePasswordToken upToken = (UsernamePasswordToken) token;  String username = upToken.getUsername();  // 从数据库中获取用户信息  User user = userService.findByUsername(username);  if (user == null) {  throw new UnknownAccountException("用户不存在");  }  // 将用户信息封装到SimpleAuthenticationInfo中,并返回  SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(  username,  user.getPassword(), // 假设密码已经加密存储  ByteSource.Util.bytes(user.getSalt()), // 假设使用了盐值加密  getName()  );  return authenticationInfo;  }  
}

3. 配置Shiro相关对象

你需要在Spring Boot的配置类中配置Shiro的相关对象,如SecurityManagerShiroFilterFactoryBean

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.spring.web.config.DefaultShiroFilterChainDefinition;  
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;  
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  import javax.servlet.Filter;  
import java.util.HashMap;  
import java.util.Map;  @Configuration  
public class ShiroConfig {  @Bean  public SecurityManager securityManager(CustomRealm customRealm) {  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  securityManager.setRealm(customRealm);  return securityManager;  }  @Bean  public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {  ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();  shiroFilterFactoryBean.setSecurityManager(securityManager);  shiroFilterFactoryBean.setLoginUrl("/login"); // 登录页面URL  shiroFilterFactoryBean.setSuccessUrl("/index"); // 登录成功后的跳转页面  shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 未授权页面URL  // 定义过滤器链  Map<String, String> filterChainDefinitionMap = new HashMap<>();  filterChainDefinitionMap.put("/static/**", "anon"); // 静态资源无需认证  filterChainDefinitionMap.put("/login", "anon"); // 登录页面无需认证  filterChainDefinitionMap.put("/**", "authc"); // 其他URL都需要认证  shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  return shiroFilterFactoryBean;  }  @Bean  public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy() {  FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();  registrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));  registrationBean.addUrlPatterns("/*");  return registrationBean;  }  @Bean  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {  AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();  advisor.setSecurityManager(securityManager);  return advisor;  }  
}


4. IndexController重写登录方法

你需要创建一个控制器来处理登录请求。

import org.apache.shiro.SecurityUtils;  
import org.apache.shiro.authc.UsernamePasswordToken;  
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.PostMapping;  
import org.springframework.web.bind.annotation.RequestParam;  @Controller  
public class IndexController {  @GetMapping("/login")  public String login() {  return "login"; // 返回登录页面  }  @PostMapping("/login")  public String login(@RequestParam("username") String username,  @RequestParam("password") String password,  Model model) {  Subject subject = SecurityUtils.getSubject();  UsernamePasswordToken token = new UsernamePasswordToken(username, password);  try {  subject.login(token);  return "redirect:/index"; // 登录成功后重定向到首页  } catch (Exception e) {  model.addAttribute("error", "用户名或密码错误");  return "login"; // 登录失败返回登录页面  }  }  @GetMapping("/index")  public String index() {  return "index"; // 返回首页  }  @GetMapping("/403")  public String accessDenied() {  return "403"; // 返回未授权页面  }  
}


5. 测试认证登录

现在,你可以启动Spring Boot应用程序,并访问/login页面来测试登录功能。输入正确的用户名和密码后,你应该会被重定向到/index页面。如果输入错误的用户名或密码,你应该会看到登录页面上的错误信息。

确保你的UserService和数据库配置正确,以便能够正确地获取和验证用户信息。此外,你还需要配置一个视图解析器(如Thymeleaf或JSP)来渲染登录页面、首页和未授权页面。


http://www.mrgr.cn/news/51724.html

相关文章:

  • 【Spring】Spring实现加法计算器和用户登录
  • conda环境打包环境、迁移环境
  • UnityAPK反编译基础修改,C#反编译
  • 力扣3194.最小元素和最大元素的平均最小值
  • mysql语句简单的使用
  • Invoke 和 InvokeRequired以及他们两个的区别
  • Linux基础命令系列一小白必掌握
  • 小O睡眠省电调研
  • 代理商培训新策略:利用内部知识库提升培训效果
  • FreeRTOS:信号量
  • 神仙级网络安全入门教程(非常详细),从零基础入门到精通,从看这篇开始!
  • 05 线性结构——队列(特性、入队与出队、顺序队列和链式队列、顺序队列的“假溢出”问题及解决方案、相关功能的定义与代码实现)
  • 集合框架10:泛型接口、泛型方法
  • gbn,sr和tcp的区别
  • EDM营销平台如何通过邮箱追踪提升效果?
  • 9.22前缀和
  • blender分离含有多个动作的模型,并导出含有材质的fbx模型
  • 条款3: 理解decltype
  • 鸿蒙跨设备协同开发05——跨设备拖拽
  • 2.4 STM32启动过程