能否脱离Spring及Spring Boot使用Spring Security?求文档或实现步骤
脱离Spring/Spring Boot实现Spring Security核心功能
Spring Security的核心是过滤器链驱动的身份认证与授权逻辑,完全可以脱离Spring容器在传统Servlet项目中实现。以下是具体的实现步骤和关键代码示例:
一、依赖管理(Maven为例)
只引入Spring Security的核心与Web模块,排除所有Spring容器相关依赖,避免和现有旧库冲突:
<dependencies> <!-- Spring Security核心 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>5.7.11</version> <!-- 选稳定的5.x版本,适配旧JDK --> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> </exclusion> </exclusions> </dependency> <!-- Spring Security Web模块,处理Servlet相关逻辑 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>5.7.11</version> <exclusions> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </exclusions> </dependency> </dependencies>
二、手动配置Security过滤器链
1. Web.xml中注册核心过滤器
在项目的web.xml中添加DelegatingFilterProxy,指定过滤器名称为springSecurityFilterChain:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 禁用Spring容器查找,手动指定过滤器实例 --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2. 手动构建过滤器链实例
创建一个初始化类(比如SecurityInitializer),在项目启动时手动构建FilterChainProxy(即springSecurityFilterChain),替代Spring容器的自动配置:
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.access.vote.RoleVoter; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.util.ArrayList; import java.util.List; public class SecurityInitializer implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // 1. 创建过滤器链列表 List<SecurityFilterChain> filterChains = new ArrayList<>(); List<javax.servlet.Filter> filters = new ArrayList<>(); // 2. 添加核心过滤器(按Spring Security固定顺序) filters.add(new SecurityContextPersistenceFilter()); // 管理SecurityContext filters.add(createUsernamePasswordAuthFilter()); // 登录认证 filters.add(createLogoutFilter()); // 登出 filters.add(createFilterSecurityInterceptor()); // 授权拦截 // 3. 绑定过滤器链到所有URL filterChains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/**"), filters)); // 4. 将FilterChainProxy注册到Servlet上下文 FilterChainProxy filterChainProxy = new FilterChainProxy(filterChains); sce.getServletContext().setAttribute("springSecurityFilterChain", filterChainProxy); } // 构建登录过滤器配置 private UsernamePasswordAuthenticationFilter createUsernamePasswordAuthFilter() { UsernamePasswordAuthenticationFilter authFilter = new UsernamePasswordAuthenticationFilter(); authFilter.setAuthenticationManager(createAuthenticationManager()); authFilter.setFilterProcessesUrl("/login"); // 登录接口URL authFilter.setAuthenticationSuccessHandler((req, res, auth) -> res.sendRedirect("/home")); // 登录成功跳转 authFilter.setAuthenticationFailureHandler((req, res, ex) -> res.sendRedirect("/login?error")); // 登录失败跳转 return authFilter; } // 构建认证管理器 private ProviderManager createAuthenticationManager() { List<org.springframework.security.authentication.AuthenticationProvider> providers = new ArrayList<>(); providers.add(new CustomAuthenticationProvider()); // 自定义认证逻辑 return new ProviderManager(providers); } // 构建登出过滤器 private LogoutFilter createLogoutFilter() { return new LogoutFilter("/login?logout", new SimpleUrlLogoutSuccessHandler()); } // 构建授权拦截器配置 private FilterSecurityInterceptor createFilterSecurityInterceptor() { FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor(); interceptor.setAuthenticationManager(createAuthenticationManager()); interceptor.setAccessDecisionManager(new AffirmativeBased(List.of(new RoleVoter()))); // 基于角色的授权 interceptor.setSecurityMetadataSource(new CustomSecurityMetadataSource()); // 自定义URL-角色映射 return interceptor; } }
然后在web.xml中注册这个监听器:
<listener> <listener-class>com.yourpackage.SecurityInitializer</listener-class> </listener>
三、实现核心业务组件
1. 自定义AuthenticationProvider(认证逻辑)
实现AuthenticationProvider接口,处理用户名密码校验:
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class CustomAuthenticationProvider implements AuthenticationProvider { private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = authentication.getCredentials().toString(); // 从数据库/自定义数据源获取用户信息(替代UserDetailsService) UserDetails user = loadUserByUsername(username); if (user == null) { throw new BadCredentialsException("User not found"); } if (!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("Invalid password"); } return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities()); } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } // 自定义用户加载逻辑,替代Spring的UserDetailsService private UserDetails loadUserByUsername(String username) { // 示例:从数据库查询用户,组装UserDetails return org.springframework.security.core.userdetails.User.builder() .username("admin") .password(passwordEncoder.encode("admin123")) .roles("ADMIN") .build(); } }
2. 自定义SecurityMetadataSource(URL-角色映射)
实现FilterInvocationSecurityMetadataSource接口,定义哪些URL需要哪些角色访问:
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.util.HashMap; import java.util.Map; import java.util.Collection; import java.util.List; public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private final Map<AntPathRequestMatcher, Collection<SimpleGrantedAuthority>> urlRolesMap = new HashMap<>(); public CustomSecurityMetadataSource() { // 初始化URL-角色映射 urlRolesMap.put(new AntPathRequestMatcher("/admin/**"), List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))); urlRolesMap.put(new AntPathRequestMatcher("/user/**"), List.of(new SimpleGrantedAuthority("ROLE_USER"))); // 公开URL不需要角色 urlRolesMap.put(new AntPathRequestMatcher("/login"), List.of()); urlRolesMap.put(new AntPathRequestMatcher("/public/**"), List.of()); } @Override public Collection<SimpleGrantedAuthority> getAttributes(Object object) throws IllegalArgumentException { FilterInvocation fi = (FilterInvocation) object; for (Map.Entry<AntPathRequestMatcher, Collection<SimpleGrantedAuthority>> entry : urlRolesMap.entrySet()) { if (entry.getKey().matches(fi.getHttpRequest())) { return entry.getValue(); } } // 默认需要登录认证 return List.of(new SimpleGrantedAuthority("ROLE_USER")); } @Override public Collection<SimpleGrantedAuthority> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } }
四、关键注意事项
- 过滤器顺序不能乱:Spring Security的过滤器有固定执行顺序,必须严格按照
SecurityContextPersistenceFilter→ 认证过滤器 → 授权过滤器的顺序添加; - 避免Spring容器依赖:所有组件都用普通Java类实现,不要使用Spring的注解(如@Autowired、@Service),完全手动管理实例;
- 密码加密必须标准化:禁止手动处理密码哈希,必须使用
PasswordEncoder接口的实现(如BCryptPasswordEncoder); - 会话管理:如果需要控制并发登录、会话超时,可添加
SessionManagementFilter和ConcurrentSessionFilter; - 异常处理:添加
ExceptionTranslationFilter统一处理AccessDeniedException和AuthenticationException,避免直接抛出到前端。
内容的提问来源于stack exchange,提问作者Sasirekha Kumaran
相关产品推荐
相关产品推荐

