SSM 权限管理整合:Spring Security 或 Shiro 与 SSM 的集成方案 – 拯救你那混乱不堪的权限系统!
各位码农朋友们,大家好!今天咱们要聊聊一个让大家头疼,却又不得不面对的问题:权限管理。
你是不是也经历过这样的噩梦?
- 混乱的代码: 权限控制逻辑散落在代码的各个角落,像一堆乱麻,改动起来让人抓狂。
- 脆弱的安全: 稍微不注意,就可能出现权限漏洞,让你的系统暴露在风险之中。
- 重复的劳动: 每个项目都要重新写一套权限控制逻辑,简直是浪费生命。
别担心,你不是一个人!今天,我就来拯救你那混乱不堪的权限系统,带你一起探索如何将 Spring Security 或 Shiro 与 SSM (Spring + SpringMVC + MyBatis) 框架完美集成,打造一套安全、高效、可维护的权限管理方案。
为什么要集成权限管理框架?
在深入技术细节之前,我们先来聊聊为什么要集成权限管理框架。难道自己写一套权限控制逻辑不好吗?
当然不好!
- 专业的事情交给专业的人做: Spring Security 和 Shiro 都是久经考验的权限管理框架,它们已经帮你处理了各种复杂的安全问题,比如身份认证、授权、会话管理等等。你只需要专注于业务逻辑,而不用操心底层的安全细节。
- 提高开发效率: 使用权限管理框架可以大大简化权限控制的开发工作,减少重复代码,提高开发效率。
- 增强系统安全性: 权限管理框架提供了各种安全机制,比如防止 CSRF 攻击、XSS 攻击等等,可以有效增强系统的安全性。
- 提高代码可维护性: 权限管理框架将权限控制逻辑从业务代码中分离出来,使得代码更加清晰、易于维护。
简单来说,选择 Spring Security 或 Shiro,就相当于站在了巨人的肩膀上,可以让你更快、更安全地构建你的应用。
Spring Security vs. Shiro:选哪个?
Spring Security 和 Shiro 都是优秀的权限管理框架,它们各有优缺点。选择哪个框架取决于你的具体需求和偏好。
特性 | Spring Security | Shiro |
---|---|---|
出身 | Spring 家族,与 Spring 框架无缝集成 | Apache 基金会,独立于 Spring 框架 |
学习曲线 | 相对陡峭,配置比较繁琐,需要对 Spring 框架有深入的了解 | 相对平缓,配置简单,易于上手 |
灵活性 | 非常灵活,可以高度定制,满足各种复杂的权限需求 | 比较灵活,但不如 Spring Security |
功能 | 功能强大,支持身份认证、授权、会话管理、防止 CSRF 攻击、XSS 攻击等等 | 功能完善,支持身份认证、授权、会话管理、加密等等 |
生态系统 | 依赖 Spring 生态系统,可以方便地与其他 Spring 组件集成 | 生态系统相对较小,但仍然可以与其他框架集成 |
使用场景 | 适合大型、复杂的应用,需要高度定制的权限管理方案,以及对 Spring 框架有深入了解的团队 | 适合中小型应用,需要快速上手、简单配置的权限管理方案 |
配置方式 | 主要通过 XML 或 Java Config 配置 | 主要通过 INI 文件或 Java Config 配置 |
总而言之,如果你对 Spring 框架非常熟悉,并且需要高度定制的权限管理方案,那么 Spring Security 是一个不错的选择。如果你希望快速上手,并且需要一个简单易用的权限管理框架,那么 Shiro 可能更适合你。
接下来,我们将分别介绍如何将 Spring Security 和 Shiro 与 SSM 框架集成。
Spring Security 与 SSM 集成
1. 添加依赖
首先,我们需要在 pom.xml
文件中添加 Spring Security 的依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>
其中 ${spring.security.version}
需要替换成你使用的 Spring Security 的版本号。
2. 配置 Spring Security
我们需要创建一个 Spring Security 的配置类,用于配置身份认证、授权规则等等。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll() // 允许匿名访问
.antMatchers("/admin/**").hasRole("ADMIN") // 需要 ADMIN 角色才能访问
.antMatchers("/user/**").hasRole("USER") // 需要 USER 角色才能访问
.anyRequest().authenticated() // 其他请求需要认证
.and()
.formLogin()
.loginPage("/login") // 自定义登录页面
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
这个配置类做了以下几件事:
- 使用
@EnableWebSecurity
注解启用 Spring Security。 - 重写
configure(HttpSecurity http)
方法,配置 HTTP 请求的安全规则。antMatchers("/public/**").permitAll()
:允许匿名访问/public/**
路径下的资源。antMatchers("/admin/**").hasRole("ADMIN")
:需要ADMIN
角色才能访问/admin/**
路径下的资源。antMatchers("/user/**").hasRole("USER")
:需要USER
角色才能访问/user/**
路径下的资源。anyRequest().authenticated()
:其他请求需要认证。formLogin().loginPage("/login").permitAll()
:使用表单登录,并指定自定义登录页面为/login
。logout().permitAll()
:允许用户注销。
- 重写
configure(AuthenticationManagerBuilder auth)
方法,配置身份认证管理器。auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder())
:使用UserDetailsService
接口获取用户信息,并使用PasswordEncoder
接口对密码进行加密。
- 创建一个
PasswordEncoder
Bean,用于对密码进行加密。
3. 实现 UserDetailsService 接口
UserDetailsService
接口用于从数据库或其他数据源获取用户信息。我们需要实现这个接口,并将其注入到 Spring Security 的配置中。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库或其他数据源获取用户信息
if ("admin".equals(username)) {
// 这里为了演示,直接写死用户信息
return new User(username, passwordEncoder.encode("123456"), new ArrayList<>());
} else {
throw new UsernameNotFoundException("User not found: " + username);
}
}
}
这个实现类做了以下几件事:
- 从数据库或其他数据源获取用户信息。
- 如果用户存在,则创建一个
UserDetails
对象,并返回。 - 如果用户不存在,则抛出一个
UsernameNotFoundException
异常。
4. 创建登录页面
我们需要创建一个登录页面,用于让用户输入用户名和密码进行登录。
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username" value="admin"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" value="123456"/>
</div>
<div>
<input type="submit" value="Login"/>
</div>
</form>
</body>
</html>
这个登录页面非常简单,只有一个用户名输入框、一个密码输入框和一个提交按钮。
5. 测试
启动你的 SSM 应用,访问 /admin
或 /user
路径,你会发现 Spring Security 会自动将你重定向到登录页面。输入用户名和密码进行登录,如果认证成功,你就可以访问 /admin
或 /user
路径下的资源了。
Shiro 与 SSM 集成
1. 添加依赖
首先,我们需要在 pom.xml
文件中添加 Shiro 的依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
其中 ${shiro.version}
需要替换成你使用的 Shiro 的版本号。
2. 配置 Shiro
我们需要创建一个 Shiro 的配置类,用于配置 Realm、安全管理器等等。
import org.apache.shiro.realm.Realm;
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.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public Realm myRealm() {
return new MyRealm();
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/login"); // 设置登录页面
shiroFilterFactoryBean.setSuccessUrl("/index"); // 设置登录成功后跳转的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 设置未授权页面
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/public/**", "anon"); // 允许匿名访问
filterChainDefinitionMap.put("/admin/**", "roles[admin]"); // 需要 admin 角色才能访问
filterChainDefinitionMap.put("/user/**", "roles[user]"); // 需要 user 角色才能访问
filterChainDefinitionMap.put("/**", "authc"); // 其他请求需要认证
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
这个配置类做了以下几件事:
- 创建一个
Realm
Bean,用于从数据库或其他数据源获取用户信息。 - 创建一个
DefaultWebSecurityManager
Bean,用于管理 Realm。 - 创建一个
ShiroFilterFactoryBean
Bean,用于配置 Shiro 过滤器。setLoginUrl("/login")
:设置登录页面为/login
。setSuccessUrl("/index")
:设置登录成功后跳转的页面为/index
。setUnauthorizedUrl("/unauthorized")
:设置未授权页面为/unauthorized
。filterChainDefinitionMap.put("/public/**", "anon")
:允许匿名访问/public/**
路径下的资源。filterChainDefinitionMap.put("/admin/**", "roles[admin]")
:需要admin
角色才能访问/admin/**
路径下的资源。filterChainDefinitionMap.put("/user/**", "roles[user]")
:需要user
角色才能访问/user/**
路径下的资源。filterChainDefinitionMap.put("/**", "authc")
:其他请求需要认证。
3. 实现 Realm 接口
Realm
接口用于从数据库或其他数据源获取用户信息和授权信息。我们需要实现这个接口,并将其注入到 Shiro 的配置中。
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 java.util.HashSet;
import java.util.Set;
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
// 从数据库或其他数据源获取用户的角色和权限
Set<String> roles = new HashSet<>();
if ("admin".equals(username)) {
roles.add("admin");
} else if ("user".equals(username)) {
roles.add("user");
}
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roles);
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
// 从数据库或其他数据源获取用户信息
if ("admin".equals(username)) {
// 这里为了演示,直接写死用户信息
return new SimpleAuthenticationInfo(username, "123456", getName());
} else if ("user".equals(username)) {
return new SimpleAuthenticationInfo(username, "123456", getName());
} else {
throw new UnknownAccountException();
}
}
}
这个实现类做了以下几件事:
- 重写
doGetAuthorizationInfo(PrincipalCollection principals)
方法,从数据库或其他数据源获取用户的角色和权限,并返回一个AuthorizationInfo
对象。 - 重写
doGetAuthenticationInfo(AuthenticationToken token)
方法,从数据库或其他数据源获取用户信息,并返回一个AuthenticationInfo
对象。
4. 创建登录页面
我们需要创建一个登录页面,用于让用户输入用户名和密码进行登录。
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username" value="admin"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" value="123456"/>
</div>
<div>
<input type="submit" value="Login"/>
</div>
</form>
</body>
</html>
这个登录页面非常简单,只有一个用户名输入框、一个密码输入框和一个提交按钮。
5. 测试
启动你的 SSM 应用,访问 /admin
或 /user
路径,你会发现 Shiro 会自动将你重定向到登录页面。输入用户名和密码进行登录,如果认证成功,你就可以访问 /admin
或 /user
路径下的资源了。
总结
今天,我们一起学习了如何将 Spring Security 和 Shiro 与 SSM 框架集成,打造一套安全、高效、可维护的权限管理方案。
无论你选择 Spring Security 还是 Shiro,都可以帮助你解决权限管理的问题,让你的代码更加清晰、易于维护,让你的系统更加安全可靠。
希望这篇文章能够帮助你摆脱权限管理的噩梦,让你的开发工作更加轻松愉快!
记住,权限管理不是一件容易的事情,需要不断学习和实践才能掌握。希望大家能够坚持学习,不断提升自己的技术水平,成为一名优秀的开发者!
最后,祝大家编程愉快!