Shiro常规用法详解:从集成到实战场景

Shiro作为一款轻量级安全框架,其常规用法涵盖了多种集成方式和典型应用场景。本文将深入讲解Shiro在实际项目中的标准用法,帮助开发者快速掌握核心配置和常见模式。

一、集成方式

1. 独立应用集成(非Spring环境)

在非Spring应用中集成Shiro需要手动创建SecurityManager实例:

// 创建SecurityManager并绑定到静态工具类
DefaultSecurityManager securityManager = new DefaultSecurityManager();
SecurityUtils.setSecurityManager(securityManager);

// 配置Realm
IniRealm realm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(realm);

实践建议:适合传统Java SE应用或遗留系统改造,但需要自行管理组件生命周期。

2. Spring/Spring Boot整合

Spring环境下通过ShiroFilterFactoryBean实现集成:

@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    factoryBean.setSecurityManager(securityManager);
    
    // 配置拦截规则
    Map<String, String> filterChain = new LinkedHashMap<>();
    filterChain.put("/login", "anon");
    filterChain.put("/admin/**", "authc, roles[admin]");
    filterChain.put("/**", "authc");
    factoryBean.setFilterChainDefinitionMap(filterChain);
    
    return factoryBean;
}

Spring Boot自动配置示例

# application.properties
shiro.enabled=true
shiro.loginUrl=/login
shiro.successUrl=/home
shiro.unauthorizedUrl=/403

二、配置示例

1. INI基础配置

# shiro.ini
[main]
# 定义Realm
myRealm = com.example.MyRealm
securityManager.realms = $myRealm

# 加密配置
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
credentialsMatcher.storedCredentialsHexEncoded = false
myRealm.credentialsMatcher = $credentialsMatcher

[urls]
/login = anon
/logout = logout
/account/** = user

2. 注解驱动安全控制

@Controller
public class AdminController {
    
    @RequiresRoles("admin")
    @GetMapping("/admin/dashboard")
    public String adminDashboard() {
        return "dashboard";
    }
    
    @RequiresPermissions("user:delete")
    @DeleteMapping("/user/{id}")
    public void deleteUser(@PathVariable Long id) {
        // 删除用户逻辑
    }
}

实践建议:注解方式适合方法级细粒度控制,但要注意AOP代理的生效条件。

三、常见场景实现

1. 登录流程定制

public class CustomFormFilter extends FormAuthenticationFilter {
    
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, 
            Subject subject, ServletRequest request, 
            ServletResponse response) throws Exception {
        // 登录成功后记录日志
        logLogin((UsernamePasswordToken)token);
        return super.onLoginSuccess(token, subject, request, response);
    }
    
    @Override
    protected boolean onLoginFailure(AuthenticationToken token,
            AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        // 登录失败处理逻辑
        return super.onLoginFailure(token, e, request, response);
    }
}

2. 动态URL权限控制

// 动态从数据库加载权限规则
@Bean
public FilterChainDefinitionMap dynamicFilterChain() {
    return new PathMatchingFilterChainDefinition() {
        @Override
        public void addPathDefinitions(String path, String definition) {
            // 从数据库查询path对应的权限规则
            List<PermissionRule> rules = permissionService.getRulesByPath(path);
            String chainDefinition = buildChainDefinition(rules);
            super.addPathDefinitions(path, chainDefinition);
        }
    };
}

3. 自定义Realm实现

public class JdbcRealm extends AuthorizingRealm {
    
    @Autowired
    private UserService userService;
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        User user = userService.findByUsername(upToken.getUsername());
        if (user == null) {
            throw new UnknownAccountException("用户不存在");
        }
        return new SimpleAuthenticationInfo(
            user.getUsername(),
            user.getPassword(),
            ByteSource.Util.bytes(user.getSalt()),
            getName()
        );
    }
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        Set<String> roles = userService.findRolesByUsername(username);
        Set<String> permissions = userService.findPermissionsByUsername(username);
        
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(roles);
        info.setStringPermissions(permissions);
        return info;
    }
}

四、扩展点实践

1. 自定义JWT Token

public class JWTToken implements AuthenticationToken {
    private String token;
    
    public JWTToken(String token) {
        this.token = token;
    }
    
    @Override
    public Object getPrincipal() {
        return JWTUtil.parseUsername(token);
    }
    
    @Override
    public Object getCredentials() {
        return token;
    }
}

// 配套的Realm
public class JWTRealm extends AuthenticatingRealm {
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        // JWT验证逻辑
    }
}

2. Redis缓存集成

@Bean
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
    return new RedisCacheManager(redisTemplate) {
        @Override
        protected long getExpireSeconds() {
            return 1800; // 30分钟过期
        }
    };
}

五、最佳实践总结

  1. 配置原则

    • 生产环境避免使用INI配置,推荐数据库存储动态规则
    • 权限规则应遵循"最严格匹配优先"原则
  2. 性能优化

图1

  1. 安全建议

    • 重要操作应结合@RequiresAuthentication@RequiresPermissions双重验证
    • 密码加密必须使用加盐哈希(如SHA-256 +随机盐值)

通过以上常规用法的系统实践,可以构建出既安全又灵活的应用权限体系。建议根据实际需求选择合适的集成方式和扩展点,避免过度设计。

评论已关闭