Để tích hợp Apache Shiro vào dự án Spring Boot, bạn cần thực hiện các bước cấu hình cơ bản sau:
1. Thêm phụ thuộc Maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.15</version>
</dependency>
</dependencies>
2. Tạo lớp Realm tùy chỉnh
package com.example.security;
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;
import org.springframework.util.StringUtils;
public class AppRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
User currentUser = userService.findByUsername(username);
SimpleAuthorizationInfo authInfo = new SimpleAuthorizationInfo();
currentUser.getRoles().forEach(role -> {
authInfo.addRole(role.getName());
role.getPermissions().forEach(perm ->
authInfo.addStringPermission(perm.getCode())
);
});
return authInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
String username = token.getPrincipal().toString();
if (StringUtils.isEmpty(username)) {
return null;
}
User user = userService.findByUsername(username);
if (user == null) {
throw new UnknownAccountException("Tài khoản không tồn tại");
}
return new SimpleAuthenticationInfo(
username,
user.getPassword(),
getName()
);
}
}
3. Cấu hình Shiro trong Spring
package com.example.config;
import org.apache.shiro.cache.ehcache.EhCacheManager;
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.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
import java.util.LinkedHashMap;
@Configuration
public class SecurityConfig {
@Bean
public EhCacheManager ehCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache/shiro-ehcache.xml");
return cacheManager;
}
@Bean
public AppRealm appRealm(EhCacheManager cacheManager) {
AppRealm realm = new AppRealm();
realm.setCacheManager(cacheManager);
return realm;
}
@Bean
public SecurityManager securityManager(AppRealm realm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(realm);
manager.setCacheManager(ehCacheManager());
return manager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
LinkedHashMap<String, String> filterChain = new LinkedHashMap<>();
filterChain.put("/login", "anon");
filterChain.put("/logout", "logout");
filterChain.put("/**", "authc");
factory.setFilterChainDefinitionMap(filterChain);
factory.setLoginUrl("/login");
factory.setSuccessUrl("/dashboard");
factory.setUnauthorizedUrl("/403");
return factory;
}
@Bean
public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Nguyên lý hoạt động
Khi người dùng đăng nhập, Shiro sẽ gọi phương thức doGetAuthenticationInfo trong Realm để xác thực thông tin đăng nhập. Sau khi đăng nhập thành công, mỗi lần truy cập tài nguyên được bảo vệ (có annotation @RequiresPermissions), Shiro sẽ gọi doGetAuthorizationInfo để kiểm tra quyền hạn.
Lưu ý: Nên bật bộ nhớ đệm (cache) để tránh gọi lại phương thức kiểm tra quyền nhiều lần không cần thiết. Có thể thay thế EhCache bằng Redis bằng cách triển khai interface CacheManager và cấu hình trong SecurityManager.