旧版Spring Security(如3.x、4.x时代)大量依赖security.xml这一XML配置文件来定义认证管理器、过滤器链以及URL访问规则。理解这些节点的含义与加载顺序,是维护老项目或做框架迁移的前提。下面以典型配置为例,拆解每个模块的职责与常见坑点。

security.xml基础结构
一个最小的security.xml通常包含http安全拦截空间和authentication-manager认证管理器。前者定义哪些URL需要什么角色,后者声明用户从哪儿加载、密码怎么校验。Spring在容器启动时会解析该文件,将其转换成一组过滤器并插入到Servlet过滤链中。
需要注意,旧版配置中命名空间一般是xmlns:security="http://www.springframework.org/schema/security",且http节点往往直接写在security命名空间下,而不是后来的java config方式。如果项目中同时存在web.xml和security.xml,要确保ContextLoaderListener已经加载了包含security.xml的Spring根容器,否则拦截器不会生效。
命名空间与Schema版本
不同Spring Security版本对应的xsd地址不同,写错会导致启动时校验失败。例如3.2.x常用http://www.springframework.org/schema/security/spring-security-3.2.xsd。建议从同版本官方包里拷贝头部声明,避免网络拉取旧schema引发兼容问题。
当security.xml与Spring MVC的dispatcherServlet配置文件分开时,安全拦截发生在更外层,所以即使controller层没做权限判断,非法请求也进不到业务方法。这种声明式防护正是XML配置的价值所在。
URL拦截规则配置
http节点内部的intercept-url用来做路径控制。它有两个核心属性:pattern和access。pattern支持Ant风格通配符,access在关闭use-expressions时是逗号分隔的角色名,开启后则是SpEL表达式。
<security:http auto-config="true" use-expressions="false">
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<security:intercept-url pattern="/user/**" access="ROLE_USER,ROLE_ADMIN" />
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</security:http>
上述配置体现了顺序敏感原则:Spring Security会按声明顺序逐个匹配,一旦命中就停止。所以细粒度路径必须写在前面,最后再用/**兜底。如果反过来,所有请求都会被匿名规则截走,后台页面将彻底裸奔。
当use-expressions设为true时,access要写成hasRole('ROLE_ADMIN')这类SpEL,不能直接写角色字符串,否则解析报错。很多老系统升级时只改了开关却没改写法,引发大面积401,这是典型的配置断裂问题。
表单登录与登出
form-login节点控制认证入口。可以指定login-page、default-target-url以及authentication-failure-url,让用户体验更可控。若auto-config为true,框架会提供默认登录页,但生产环境通常自定义。
<security:http>
<security:form-login
login-page="/login.jsp"
default-target-url="/home"
authentication-failure-url="/login.jsp?error=1" />
<security:logout logout-url="/logout" logout-success-url="/login.jsp" />
</security:http>
logout节点默认会清理Session并清除Remember-Mecookie。如果前端是前后端分离的老系统,可能要通过ajax触发/logout,此时要确保CSRF防护配置不会把退出请求拦掉。旧版默认CSRF是关闭的,但4.0后默认开启,混用配置时容易踩坑。
认证管理器与用户来源
authentication-manager负责拿到用户输入的凭证并校验。最常用的是内部嵌一个authentication-provider,再挂user-service。user-service既能写死内存用户,也能通过jdbc-user-service查库。
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="tom" password="tom123" authorities="ROLE_USER" />
<security:user name="admin" password="root" authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
内存用户仅适合演示或测试。真实项目多用jdbc-user-service并指定users-by-username-query与authorities-by-username-query两条SQL。此时密码字段若存的是明文,需要在password-encoder节点显式配置<security:password-encoder hash="plaintext"/>,否则框架默认会用随机盐加密比对,直接登录失败。
如果系统接入的是LDAP或自定义凭证源,可以换成ldap-authentication-provider或自己写AuthenticationProvider实现类,再通过<bean>引到authentication-manager里。XML的灵活性正体现在这些可插拔节点上。
密码加密与升级
旧版常用的SHA或者BCrypt都可以在password-encoder里声明。例如BCrypt写法:
<security:password-encoder ref="bcryptEncoder" /> <bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
这样用户在库里存的是BCrypt哈希,登录时框架自动用同一算法校验。要注意的是,老系统如果从明文迁移,必须先把存量密码批量加密,否则旧密码全部失效。可以在低峰期写脚本处理,再切encoder配置。
常见问题与排查思路
配置写完后若页面一直重定向到登录页,先确认intercept-url顺序与角色拼写;若启动报xsd找不到,检查schema版本与网络;若登录总提示凭证错误,多半是password-encoder不匹配。借助日志把org.springframework.security设为DEBUG,能看到过滤器链匹配细节。
另一个隐蔽问题是多个security.xml被重复加载,导致bean定义冲突。Maven聚合项目里,子模块各自带一份security.xml又没用通配符排除,就会让authentication-manager被定义两次。用ContextLoader的配置文件通配能缓解该问题。
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 所有请求跳登录页 | intercept-url顺序颠倒 | 细粒度规则前置 |
| 登录报凭证错误 | encoder与库密码算法不一 | 统一password-encoder |
| 启动xsd报错 | schema版本错配 | 对齐Security版本 |
整体来看,security.xml虽显陈旧,但节点清晰、改动直观。掌握http、authentication-manager以及各类provider的协作方式,就能在不动源码的前提下,快速调整老系统的安全策略。
Spring_Securitysecurity.xmlXML配置修改时间:2026-08-07 10:24:36