spring security 2.05总结

# spring security总结  
# 首先导入spring security所需要的jar包  
# spring-security-core-2.0.5.RELEASE.jar  
# spring-security-core-tiger-2.0.5.RELEASE.jar  
# 一.配置过滤器  
# 在web.xml中定义如下过滤器  
#   <filter>  
#         <filter-name>springSecurityFilterChain</filter-name>  
#         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
#     </filter>  
#     <filter-mapping>  
#         <filter-name>springSecurityFilterChain</filter-name>  
#         <url-pattern>/*</url-pattern> 
#     </filter-mapping> 
# 二.在spring配置文件中添加security的命名空间 
# <beans:beans xmlns="http://www.springframework.org/schema/security" 
#     xmlns:beans="http://www.springframework.org/schema/beans" 
#     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
#     xsi:schemaLocation="http://www.springframework.org/schema/beans 
#     http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
#     http://www.springframework.org/schema/security 
#     http://www.springframework.org/schema/security/spring-security-2.0.4.xsd"> 
# 三.在spring配置文中中定义需要保护的资源 
#     <http auto-config='true'> 
#         <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> 
#         <intercept-url pattern="/**" access="ROLE_USER" /> 
#     </http> 
#     注意intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。 
# 四.使用数据库获取用户权限 
#   <!-- 数据源 --> 
#     <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
#     <beans:property name="driverClassName" value="com.mysql.jdbc.Driver"></beans:property> 
# <beans:property name="url" value="jdbc:mysql://localhost:3306/sp"></beans:property> 
# <beans:property name="username" value="root"></beans:property> 
# <beans:property name="password" value="root"></beans:property> 
#     </beans:bean> 
#     <!-- 定义用户的权限根据注入的数据源获得 --> 
#     <authentication-provider> 
#         <jdbc-user-service data-source-ref="dataSource"/> 
#     </authentication-provider> 
#  
# 定义权限管理模块的表结构(mysql数据库) 
## 将资源信息放入数据库中  
# 表结构  
# /* 用户表 */  
# CREATE TABLE `t_account` (  
#   `id` int(11) NOT NULL,  
#   `username` varchar(255) default NULL,  
#   `password` varchar(255) default NULL,  
#   `enabled` int default NULL, /* 用户是否禁用 0:禁用 非0:可用*/  
#   PRIMARY KEY  (`id`)  
#     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
#     /* 角色表 */  
#     CREATE TABLE `t_role` (  
#   `id` int(11) NOT NULL,  
#   `name` varchar(255) default NULL, /* 角色名 */  
#   `descn` varchar(255) default NULL, /* 角色在spring配置文件中的名字 如ROLE_ADMIN,ROLE_USER*/  
#   PRIMARY KEY  (`id`)  
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
#   
# /* 用户角色中间表 */  
# CREATE TABLE `t_account_role` (  
#   `a_id` int(11) NOT NULL,  
#   `r_id` int(11) NOT NULL,  
#   PRIMARY KEY  (`a_id`,`r_id`),  
#   KEY `FK1C2BC9332D31C656` (`r_id`),  
#   KEY `FK1C2BC93371CCC630` (`a_id`),  
#   CONSTRAINT `FK1C2BC93384B0A30E` FOREIGN KEY (`a_id`) REFERENCES `t_account` (`id`),  
#   CONSTRAINT `FK1C2BC9332D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)  
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
# /* 资源表 */  
# CREATE TABLE `t_module` (  
#   `id` int(11) NOT NULL,  
#   `name` varchar(255) default NULL,  
#   `address` varchar(255) default NULL,  
#   PRIMARY KEY  (`id`)  
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
#   
# /* 资源角色的中间表 */  
# CREATE TABLE `t_module_role` (  
#   `m_id` int(11) NOT NULL,  
#   `r_id` int(11) NOT NULL,  
#   PRIMARY KEY  (`m_id`,`r_id`),  
#   KEY `FKA713071E2D31C656` (`r_id`),  
#   KEY `FKA713071ED78C9071` (`m_id`),  
#   CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`),  
#   CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)  
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
#   
# /* 初始化数据 */  
# insert into t_account values(1,'zhangsan','123',1);  
# insert into t_account values(2,'lisi','321',1);  
#   
# insert into t_role values(1,'系统管理员','ROLE_ADMIN');  
# insert into t_role values(2,'普通用户','ROLE_USER');  
#   
# insert into t_account_role values(1,2);  
#     insert into t_account_role values(2,1);  
#      
#     insert into t_module values(1,'部门管理','/dept.jsp');  
#     insert into t_module values(2,'人员管理','/emp.jsp');  
#      
#     insert into `t_module_role` values(1,1);  
#     insert into `t_module_role` values(1,2);  
#     insert into `t_module_role` values(2,1);  
#      
# 当用户登录时,spring security首先判断用户是否可以登录。用户登录后spring security获得该用户的  
# 所有权限以判断用户是否可以访问资源。  
#   
# spring配置文件中定义  
# <authentication-provider>  
#         <jdbc-user-service data-source-ref="dataSource"  
#         users-by-username-query="select username,password,enabled from t_account where username=?"  
#         authorities-by-username-query="select r.descn from t_account_role ar join  
#          t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>  
#     </authentication-provider>  
#      
#     users-by-username-query:根据用户名查找用户  
#     authorities-by-username-query:根据用户名查找这个用户所有的角色名,将用户访问的URL地址和  
#     查询结果与<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />标签进行匹配。  
#     匹配成功就允许访问,否则就返回到提示页面。  
#      
#     在<http>标签中添加登录页面等信息  
#     <http auto-config='true'>  
#       <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>  
#         <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />  
#         <intercept-url pattern="/**" access="ROLE_USER" />  
#         <form-login login-page="/login.jsp"  
#                     authentication-failure-url="/error.jsp"  
#                     default-target-url="/index.jsp" />  
#     </http>     
#      
#     <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>让没有登录的用户也可以访问登录页面  
#     <form-login login-page="/login.jsp"  
#                     authentication-failure-url="/error.jsp"  
#                     default-target-url="/index.jsp" />  
#     login-page:当用户登录时显示自定义登录页面  
#     authentication-failure-url:登录失败时跳转到哪个页面  
#     default-target-url:登录成功后跳转到哪个页面  
#      
#     注意:users-by-username-query指定的查询,必须至少按顺序返回3列,列名必须是 username,password,enabled  
#           authorities-by-username-query指定的查询,必须至少按顺序返回2列,第一列列名必须是username  
#           第2列必须是权限的名字,与<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />中的  
#           access匹配。  
#           不能使用select *  
#     完成的配置文件:  
#     <?xml version="1.0" encoding="UTF-8"?>  
# <beans:beans xmlns="http://www.springframework.org/schema/security"  
#     xmlns:beans="http://www.springframework.org/schema/beans"  
#     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
#     xsi:schemaLocation="http://www.springframework.org/schema/beans  
#     http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
#     http://www.springframework.org/schema/security  
#     http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">  
#      
#     <!-- 数据源 -->  
#     <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
#     <beans:property name="driverClassName" value="com.mysql.jdbc.Driver"></beans:property>  
# <beans:property name="url" value="jdbc:mysql://localhost:3306/sp"></beans:property>  
# <beans:property name="username" value="root"></beans:property>  
# <beans:property name="password" value="root"></beans:property>  
#     </beans:bean>  
#   
#     <http auto-config='true'>  
#     <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>  
#         <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />  
#         <intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />  
#         <form-login login-page="/login.jsp"  
#                     authentication-failure-url="/error.jsp"  
#                     default-target-url="/index.jsp" />  
#     </http>  
#   
#     <authentication-provider>  
#         <jdbc-user-service data-source-ref="dataSource"  
#         users-by-username-query="select username,password,enabled from t_account where username=?"  
#         authorities-by-username-query="select a.username,r.descn from t_account_role ar join  
#          t_account a on ar.a_id=a.id join t_role r on ar.r_id=r.id where a.username=?"/>  
#     </authentication-provider>  
#   
# </beans:beans>  
#   
# 登录页面  
# <form action="${pageContext.request.contextPath}/j_spring_security_check" method="post">  
#         用户: <input type="text" name="j_username" value="${SPRING_SECURITY_LAST_USERNAME}"/><br />  
#         密码: <input type="password" name="j_password"/><br />  
#         <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆<br />  
#         <input type="submit" value="登陆"/><input type="reset" value="重置"/>  
#   </form>  
#   页面中输入控件的name属性和form的action地址必须符合spring security的规定  
#    
#    
# 登录失败页面  
#   ${SPRING_SECURITY_LAST_EXCEPTION.message}    获取spring生成的异常  
#    
# 1.在自定义的过滤器中获取资源的URL地址和角色名以取代spring配置文件中原有的<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>  
#     过滤器代码:  
# import java.sql.ResultSet;  
# import java.sql.SQLException;  
#   
# import java.util.LinkedHashMap;  
# import java.util.List;  
# import java.util.Map;  
#   
# import javax.sql.DataSource;  
#   
# import org.springframework.beans.factory.FactoryBean;  
#   
# import org.springframework.jdbc.core.support.JdbcDaoSupport;  
# import org.springframework.jdbc.object.MappingSqlQuery;  
#   
# import org.springframework.security.ConfigAttributeDefinition;  
# import org.springframework.security.ConfigAttributeEditor;  
# import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;  
# import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;  
# import org.springframework.security.intercept.web.RequestKey;  
# import org.springframework.security.util.AntUrlPathMatcher;  
# import org.springframework.security.util.UrlMatcher;  
#   
#   
# public class JdbcFilterInvocationDefinitionSourceFactoryBean  
#     extends JdbcDaoSupport implements FactoryBean {  
#     private String resourceQuery;  
#   
#     public boolean isSingleton() {  
#         return true;  
#     }  
#   
#     public Class getObjectType() {  
#         return FilterInvocationDefinitionSource.class;  
#     }  
#   
#     public Object getObject() {  
#         return new DefaultFilterInvocationDefinitionSource(this  
#             .getUrlMatcher(), this.buildRequestMap());  
#     }  
#   
#     protected Map<String, String> findResources() {  
#         ResourceMapping resourceMapping = new ResourceMapping(getDataSource(),  
#                 resourceQuery);  
#   
#         Map<String, String> resourceMap = new LinkedHashMap<String, String>();  
#   
#         for (Resource resource : (List<Resource>) resourceMapping.execute()) {  
#             String url = resource.getUrl();  
#             String role = resource.getRole();  
#   
#             if (resourceMap.containsKey(url)) {  
#                 String value = resourceMap.get(url);  
#                 resourceMap.put(url, value + "," + role);  
#             } else {  
#                 resourceMap.put(url, role);  
#             }  
#         }  
#   
#         return resourceMap;  
#     }  
#   
#     protected LinkedHashMap<RequestKey, ConfigAttributeDefinition> buildRequestMap() {  
#         LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;  
#         requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();  
#   
#         ConfigAttributeEditor editor = new ConfigAttributeEditor();  
#   
#         Map<String, String> resourceMap = this.findResources();  
#   
#         for (Map.Entry<String, String> entry : resourceMap.entrySet()) {  
#             RequestKey key = new RequestKey(entry.getKey(), null);  
#             editor.setAsText(entry.getValue());  
#             requestMap.put(key,  
#                 (ConfigAttributeDefinition) editor.getValue());  
#         }  
#   
#         return requestMap;  
#     }  
#   
#     protected UrlMatcher getUrlMatcher() {  
#         return new AntUrlPathMatcher();  
#     }  
#   
#     public void setResourceQuery(String resourceQuery) {  
#         this.resourceQuery = resourceQuery;  
#     }  
#   
#     private class Resource {  
#         private String url;  
#         private String role;  
#   
#         public Resource(String url, String role) {  
#             this.url = url;  
#             this.role = role;  
#         }  
#   
#         public String getUrl() {  
#             return url;  
#         }  
#   
#         public String getRole() {  
#             return role;  
#         }  
#     }  
#   
#     private class ResourceMapping extends MappingSqlQuery {  
#         protected ResourceMapping(DataSource dataSource,  
#             String resourceQuery) {  
#             super(dataSource, resourceQuery);  
#             compile();  
#         }  
#   
#         protected Object mapRow(ResultSet rs, int rownum)  
#             throws SQLException {  
#             String url = rs.getString(1);  
#             String role = rs.getString(2);  
#             Resource resource = new Resource(url, role);  
#              
#             return resource;  
#         }  
#     }  
# }  
#   
# 将自定义的过滤器放入到原有的spring security过滤器链中(在spring配置文件中配置)  
# 定义自定义过滤器(sql语句用于查询资源的URL地址 如:/index.jsp 和角色名 如ROLE_USER)  
# <beans:bean id="filterInvocationDefinitionSource"  
#         class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">  
#         <beans:property name="dataSource" ref="dataSource"/>  
#         <beans:property name="resourceQuery" value="  
#             select m.address,r.descn  
# from t_module_role mr  
# join t_module m on mr.m_id=m.id  
# join t_role r on mr.r_id=r.id;  
#         "/>  
#     </beans:bean>  
#   将自定义过滤器放入过滤器链中  
#   <beans:bean id="filterSecurityInterceptor"  
#         class="org.springframework.security.intercept.web.FilterSecurityInterceptor" autowire="byType">  
#         <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>  
#         <beans:property name="objectDefinitionSource" ref="filterInvocationDefinitionSource" />  
#     </beans:bean>  
#     注意:FilterSecurityInterceptor过滤器会向request中写入一个标记,用于标记是否已经控制了当前请求,以避免对同一请求多次处理,导致第2个FilterSecurityInterceptor不会再次执行。  
#     在<http>中不需要再定义<intercept-url>,如下:  
#     <http auto-config='true'>  
#         <form-login login-page="/login.jsp"  
#                     authentication-failure-url="/error.jsp"  
#                     default-target-url="/index.jsp" />  
#                      
#     </http>  
#     自定义的过滤器就从配置文件中读取sql,查询结果就是角色和资源,用户登录时就在session中保存了用户的角色。  
#     注意:intercept-url的先后顺序,spring security使用第一个能匹配的intercept-url标签进行权限控制。  
#     现在intercept-url来源于数据库,所以在sql查询时注意角色和资源的顺序。  
#     建议在角色和资源的中间表中添加1个字段用于标识顺序,(按从严到宽的顺序)  
#     表结构修改如下:  
#     /* 资源角色的中间表 */  
# CREATE TABLE `t_module_role` (  
#   `m_id` int(11) NOT NULL,  
#   `r_id` int(11) NOT NULL,  
#   `priority` int(11) default NULL, /* 用于标识角色和资源的匹配顺序 从严到宽 */  
#   PRIMARY KEY  (`m_id`,`r_id`),  
#   KEY `FKA713071E2D31C656` (`r_id`),  
#   KEY `FKA713071ED78C9071` (`m_id`),  
#   CONSTRAINT `FKA713071ED78C9071` FOREIGN KEY (`m_id`) REFERENCES `t_module` (`id`),  
#   CONSTRAINT `FKA713071E2D31C656` FOREIGN KEY (`r_id`) REFERENCES `t_role` (`id`)  
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
#   
# 数据如下:  
# insert into t_account values(1,'zhangsan','123',1);  
# insert into t_account values(2,'lisi','321',1);  
#   
# insert into t_role values(1,'系统管理员','ROLE_ADMIN');  
# insert into t_role values(2,'普通用户','ROLE_USER');  
#   
# insert into t_account_role values(1,2);  
#     insert into t_account_role values(2,1);  
#      
#     insert into t_module values(1,'部门管理','/dept.jsp');  
#     insert into t_module values(2,'人员管理','/emp.jsp');  
#      
#     insert into `t_module_role` values(1,1,3);  
#     insert into `t_module_role` values(1,2,2);  
#     insert into `t_module_role` values(2,1,1);  
#      
#     自定义过滤器修改如下:  
#     <beans:bean id="filterInvocationDefinitionSource"  
#         class="com.lovo.JdbcFilterInvocationDefinitionSourceFactoryBean">  
#         <beans:property name="dataSource" ref="dataSource"/>  
#         <beans:property name="resourceQuery" value="  
#             select m.address,r.descn  
# from t_module_role mr  
# join t_module m on mr.m_id=m.id  
# join t_role r on mr.r_id=r.id  
# order by mr.priority  
#         "/>  
#     </beans:bean>  
#      
#     如果持久层使用的是hibernate,那么只需要给自定义过滤器注入1个hibernateTemplate.  
#     在自定义过滤器中就不需要再使用mapRow的方式。而是直接获取url和角色名即可。  
#     例如:  
#     public class ModuleFilter implements FactoryBean {  
# private String resourceQuery;  
#   
#     public boolean isSingleton() {  
#         return true;  
#     }  
#   
#     public Class getObjectType() {  
#         return FilterInvocationDefinitionSource.class;  
#     }  
#   
#     public Object getObject() {  
#         return new DefaultFilterInvocationDefinitionSource(this  
#             .getUrlMatcher(), this.buildRequestMap());  
#     }  
#   
#     protected Map<String, String> findResources() {  
#         ResourceMapping resourceMapping = new ResourceMapping();  
#   
#         Map<String, String> resourceMap = new LinkedHashMap<String, String>();  
#   
#         for (Resource resource : (List<Resource>) resourceMapping.execute()) {  
#             String url = resource.getUrl();  
#             String role = resource.getRole();  
#   
#             if (resourceMap.containsKey(url)) {  
#                 String value = resourceMap.get(url);  
#                 resourceMap.put(url, value + "," + role);  
#             } else {  
#                 resourceMap.put(url, role);  
#             }  
#         }  
#   
#         return resourceMap;  
#     }  
#   
#     protected LinkedHashMap<RequestKey, ConfigAttributeDefinition> buildRequestMap() {  
#         LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap = null;  
#         requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();  
#   
#         ConfigAttributeEditor editor = new ConfigAttributeEditor();  
#   
#         Map<String, String> resourceMap = this.findResources();  
#   
#         for (Map.Entry<String, String> entry : resourceMap.entrySet()) {  
#             RequestKey key = new RequestKey(entry.getKey(), null);  
#             editor.setAsText(entry.getValue());  
#             requestMap.put(key,  
#                 (ConfigAttributeDefinition) editor.getValue());  
#         }  
#   
#         return requestMap;  
#     }  
#   
#     protected UrlMatcher getUrlMatcher() {  
#         return new AntUrlPathMatcher();  
#     }  
#   
#     public void setResourceQuery(String resourceQuery) {  
#         this.resourceQuery = resourceQuery;  
#     }  
#   
#     private class Resource {  
#         private String url;  
#         private String role;  
#   
#         public Resource(String url, String role) {  
#             this.url = url;  
#             this.role = role;  
#         }  
#   
#         public String getUrl() {  
#             return url;  
#         }  
#   
#         public String getRole() {  
#             return role;  
#         }  
#     }  
#   
#     private class ResourceMapping{  
#         public List<Resource> execute(){  
#         List<Resource> rlist = new ArrayList<Resource>();  
#         List<Role> list = hibernateTemplate.find(resourceQuery);  
#         for(int i=0;i<list.size();i++){  
#         Role role = list.get(i);  
#         Set<Module> set = role.getModuleSet();  
#         Iterator<Module> it = set.iterator();  
#         while(it.hasNext()){  
#         Module m = it.next();  
#         Resource re = new Resource(m.getUrl(),role.getDescn());  
#         rlist.add(re);  
#         }  
#         }  
# return rlist;  
#         }  
#     }  
#   
#     public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {  
# this.hibernateTemplate = hibernateTemplate;  
# }  
#   
# private HibernateTemplate hibernateTemplate;  
# }  
# 而从灵活性的角度考虑,把hql写在配置文件中  
# <beans:bean id="moduleFilter" class="com.lovo.ModuleFilter">  
#          <beans:property name="resourceQuery"  
#          value="from com.lovo.po.Role order by ind">  
#          </beans:property>  
#          <beans:property name="hibernateTemplate" ref="hibernateTemplate">  
#          </beans:property>  
#     </beans:bean>  
#      
#     问题:系统只会在初始化的时候从数据库中加载信息。无法识别数据库中信息的改变。  
#     解决:每个jsp页面上重新内存  
#     代码:每个jsp页面include如下代码:  
# <%@page import="org.springframework.context.ApplicationContext"%>  
# <%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>  
# <%@page import="org.springframework.beans.factory.FactoryBean"%>  
# <%@page import="org.springframework.security.intercept.web.FilterSecurityInterceptor"%>  
# <%@page import="org.springframework.security.intercept.web.FilterInvocationDefinitionSource"%>  
# <%  
#     ApplicationContext ctx =  WebApplicationContextUtils.getWebApplicationContext(application);  
#     FactoryBean factoryBean = (FactoryBean) ctx.getBean("&自定义过滤器的id");  
#     FilterInvocationDefinitionSource fids = (FilterInvocationDefinitionSource) factoryBean.getObject();  
#     FilterSecurityInterceptor filter = (FilterSecurityInterceptor) ctx.getBean("filterSecurityInterceptor");  
#     filter.setObjectDefinitionSource(fids);  
# %>  
#   

或者加入
// 刷新SpringSecurity权限信息
	private void flushSpringSecurity() {
		try {
			FactoryBean factoryBean = (FactoryBean)SpringUtil.getBean("&userSecurityDefinitionSource");
			FilterInvocationDefinitionSource filterInvocationDefinitionSource = (FilterInvocationDefinitionSource) factoryBean.getObject();
		    FilterSecurityInterceptor filterSecurityInterceptor = (FilterSecurityInterceptor) SpringUtil.getBean("filterSecurityInterceptor");
		    filterSecurityInterceptor.setObjectDefinitionSource(filterInvocationDefinitionSource);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 

你可能感兴趣的:(Spring Security)