引言
在现代的Web应用程序中,权限管理是一个非常重要的功能。它可以确保只有授权用户可以访问特定的资源和操作。SpringBoot是一个无配置的快速开发框架,Redis是一个高性能的键值存储数据库,而Shiro是一个用于身份验证、授权和加密的开源Java安全框架。本文将介绍如何使用SpringBoot、Redis和Shiro结合实现强大的权限管理系统。
1. 创建SpringBoot项目
首先,我们需要创建一个SpringBoot项目。可以使用SpringInitializr或者手动创建一个空的Maven项目。
2. 配置Redis
在SpringBoot项目中,我们可以通过添加Redis相关的依赖来实现与Redis的集成。在pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
接下来,在application.properties
文件中配置Redis的连接信息:
spring.redis.host=localhost
spring.redis.port=6379
3. 集成Shiro
Shiro提供了一套完整的身份验证、授权和加密解决方案。我们可以通过添加Shiro相关的依赖来集成Shiro。在pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
接下来,我们需要创建一个ShiroConfig
类来配置Shiro:
@Configuration
public class ShiroConfig {
@Autowired
private RedisCacheManager redisCacheManager;
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 设置静态资源不需要权限验证
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
// 设置登录接口不需要权限验证
filterChainDefinitionMap.put("/login", "anon");
// 设置其他接口需要权限验证
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisConnectionFactory(redisConnectionFactory);
return redisCacheManager;
}
@Bean
public SecurityManager securityManager(Realm realm, SessionsSecurityManager sessionsSecurityManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
securityManager.setCacheManager(redisCacheManager);
securityManager.setSessionManager(sessionsSecurityManager);
return securityManager;
}
@Bean
public SessionsSecurityManager sessionsSecurityManager(RedisCacheManager redisCacheManager) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setCacheManager(redisCacheManager);
sessionManager.setGlobalSessionTimeout(1800000); // 30 minutes
sessionManager.setDeleteInvalidSessions(true);
return sessionManager;
}
@Bean
public Realm realm(CredentialsMatcher credentialsMatcher) {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(credentialsMatcher);
return userRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
}
在上面的配置中,我们通过redisCacheManager
配置了Redis作为缓存,设置了登录接口和静态资源不需要权限验证,其他接口需要权限验证。我们同时配置了sessionManager
来管理用户的会话信息。
接下来,我们需要创建一个自定义的Realm
类来提供用户信息和权限信息。这里我创建一个UserRealm
类继承AuthorizingRealm
,并实现其中的方法:
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String username = (String) getAvailablePrincipal(principals);
User user = userService.getUserByUsername(username);
if (user != null) {
Set<String> roles = userService.getUserRoles(user.getId());
authorizationInfo.setRoles(roles);
Set<String> permissions = userService.getUserPermissions(user.getId());
authorizationInfo.setStringPermissions(permissions);
}
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException();
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
return authenticationInfo;
}
}
在上面的代码中,doGetAuthorizationInfo
方法用于获取用户的角色和权限信息,doGetAuthenticationInfo
方法用于验证用户的身份信息。
4. 实现登录和鉴权接口
我们需要创建一些接口来实现用户的登录和鉴权。
首先,我们需要创建一个LoginController
类来处理登录逻辑:
@RestController
public class LoginController {
@Autowired
private UserService userService;
@Autowired
private RedisTemplate<String, Serializable> redisTemplate;
@PostMapping("/login")
public String login(String username, String password) {
User user = userService.getUserByUsername(username);
if (user == null || !user.getPassword().equals(password)) {
return "登录失败";
}
// 将用户信息存储到Redis中
redisTemplate.boundValueOps(username).set(user);
return "登录成功";
}
@GetMapping("/unauthorized")
public String unauthorized() {
return "未经授权";
}
}
上面的代码中,我们使用RedisTemplate
将用户信息存储到Redis中,以便于后续的鉴权操作。
接下来,我们需要为一些需要鉴权的接口添加@RequiresAuthentication
和@RequiresRoles
或@RequiresPermissions
注解来限制访问:
@RestController
public class HelloController {
@GetMapping("/hello")
@RequiresAuthentication
@RequiresRoles("admin")
public String hello() {
return "Hello, World!";
}
}
在上面的代码中,我们使用了@RequiresAuthentication
注解要求用户进行身份验证,@RequiresRoles
注解要求用户具有指定的角色,@RequiresPermissions
注解要求用户具有指定的权限。
5. 测试
现在,我们可以启动SpringBoot应用程序,并使用浏览器或者API测试工具来测试接口。首先访问/login
接口进行登录,然后访问/hello
接口进行鉴权请求。如果用户登录成功且具有相应的角色和权限,将会返回"Hello, World!",否则将会返回"未经授权"。
总结
在本文中,我们介绍了如何使用SpringBoot、Redis和Shiro实现权限管理。通过集成Shiro,我们可以方便地进行用户身份验证和授权操作。同时,通过使用Redis作为缓存,可以提高系统的性能和并发能力。希望本文对您有所帮助,并能帮助您构建一个安全可靠的权限管理系统。
本文来自极简博客,作者:风华绝代,转载请注明原文链接:用SpringBoot、Redis和Shiro实现权限管理