Apache Shiro 是一个强大且灵活的 Java 安全框架,用于处理身份验证、授权、会话管理和加密,在 Shiro 中,Realm 是核心组件之一,它负责与数据源(如数据库、LDAP 等)进行交互以获取用户信息和权限数据。
本手册将介绍如何实现自定义 Realm,以便更好地理解 Shiro 的工作原理。
什么是 Realm?
Realm 是 Shiro 中的一个接口,定义了如何获取用户的身份信息和权限数据,Shiro 提供了一些内置的 Realm 实现,例如IniRealm
、JdbcRealm
、LdapRealm
等,但在实际项目中,我们通常需要自定义 Realm 来满足特定的需求。
实现自定义 Realm
要实现自定义 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; public class MyCustomRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获取当前登录的用户 String username = (String) principals.getPrimaryPrincipal(); // 根据用户名从数据库或其他数据源获取角色和权限信息 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 添加角色 authorizationInfo.addRole("admin"); // 添加权限 authorizationInfo.addStringPermission("user:read"); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 获取基于用户名和密码的令牌 UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = new String(upToken.getPassword()); // 从数据库或其他数据源获取用户信息 // 这里假设用户名为 "admin",密码为 "password" if ("admin".equals(username)) { return new SimpleAuthenticationInfo(username, password, getName()); } else { throw new UnknownAccountException("用户名或密码错误"); } } }
配置 Shiro
在 Shiro 配置文件中,我们需要指定自定义的 Realm,以下是一个使用shiro.ini
文件的配置示例:
[main] myCustomRealm = com.example.MyCustomRealm securityManager.realms = $myCustomRealm
或者,如果你使用的是 Spring Boot,可以在配置类中进行配置:
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ShiroConfig { @Bean public MyCustomRealm myCustomRealm() { return new MyCustomRealm(); } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myCustomRealm()); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 配置过滤器链 return shiroFilterFactoryBean; } }
测试自定义 Realm
我们可以编写一个简单的测试用例来验证自定义 Realm 是否工作正常:
import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.subject.Subject; import org.junit.Assert; import org.junit.Test; public class MyCustomRealmTest { @Test public void testLogin() { // 设置安全管理器 SecurityUtils.setSecurityManager(new DefaultWebSecurityManager(new MyCustomRealm())); Subject subject = SecurityUtils.getSubject(); // 创建用户名/密码令牌 UsernamePasswordToken token = new UsernamePasswordToken("admin", "password"); try { // 执行登录操作 subject.login(token); // 检查是否认证成功 Assert.assertTrue(subject.isAuthenticated()); // 检查是否有特定角色 Assert.assertTrue(subject.hasRole("admin")); // 检查是否有特定权限 Assert.assertTrue(subject.isPermitted("user:read")); } catch (AuthenticationException e) { e.printStackTrace(); Assert.fail("登录失败"); } finally { // 注销登录 subject.logout(); } } }
通过以上步骤,你可以实现一个自定义的 Realm,并将其集成到 Shiro 的安全框架中,希望这个指南对你有所帮助!