我最近一直在研究Spring安全性,我需要知道如何使用数据库动态定义intercept-url(在Spring Security中)。
我已经深入挖掘了整个互联网,但在这方面我找不到任何独特的(当然也是有用的)教程。
所以这就是我所做的:
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL(url);
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
public List<ConfigAttribute> getAttributesByURL(String inputUrl)
{
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
Connection connection = null;
String url = "jdbc:mysql://173.0.0.22:3306/";
String dbName = "kheirkhahandb";
String driverName = "com.mysql.jdbc.Driver";
String userName = "kheirkhahan";
String password = "kheirkhahan";
try{
Class.forName(driverName).newInstance();
connection = DriverManager.getConnection(url+dbName, userName, password);
try{
Statement stmt = connection.createStatement();
String selectquery = "select * from URL_ACCESS where URL = '" + inputUrl +"'";
ResultSet rs = stmt.executeQuery(selectquery);
while(rs.next()){
MyConfigAttribute temp = new MyConfigAttribute();
String attr = rs.getString("ACCESS").toString();
temp.setAttr(attr);
attributes.add(temp);
}
}
catch(SQLException s){
System.out.println(s);
}
connection.close();
}
catch (Exception e){
e.printStackTrace();
}
return attributes;
}
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL(url);
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
public List<ConfigAttribute> getAttributesByURL(String inputUrl)
{
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
Connection connection = null;
String url = "jdbc:mysql://173.0.0.22:3306/";
String dbName = "kheirkhahandb";
String driverName = "com.mysql.jdbc.Driver";
String userName = "kheirkhahan";
String password = "kheirkhahan";
try{
Class.forName(driverName).newInstance();
connection = DriverManager.getConnection(url+dbName, userName, password);
try{
Statement stmt = connection.createStatement();
String selectquery = "select * from URL_ACCESS where URL = '" + inputUrl +"'";
ResultSet rs = stmt.executeQuery(selectquery);
while(rs.next()){
MyConfigAttribute temp = new MyConfigAttribute();
String attr = rs.getString("ACCESS").toString();
temp.setAttr(attr);
attributes.add(temp);
}
}
catch(SQLException s){
System.out.println(s);
}
connection.close();
}
catch (Exception e){
e.printStackTrace();
}
return attributes;
}
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
</bean>
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
<property name="accessDeniedHandler" ref="accessDeniedHandler" />
</bean>
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp?error=entryPoint" />
</bean>
<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/login.jsp?error=access_denied" />
</bean>
<bean id="authenticationProcessingFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
</bean>
<bean id="myFilterInvocationSecurityMetadataSource" class="com.datx.dao.MyFilterSecurityMetadataSource">
</bean>
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/login.jsp?error=logout" />
<constructor-arg ref="logoutHandler">
</constructor-arg>
</bean>
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"></bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
<sec:jdbc-user-service data-source-ref="dataSource"
group-authorities-by-username-query="
SELECT acg.ID, acg.GROUP_NAME, a.AUTHORITY_NAME AS AUTHORITY
FROM ACCESS_GROUPS acg, ACCESS_GROUP_MEMBERSHIP agm, GROUP_AUTHORITIES ga, AUTHORITIES a
WHERE agm.USERNAME = ? and acg.ID = ga.GROUP_ID and acg.ID = agm.GROUP_ID and ga.AUTHORITY_ID = a.ID
"
users-by-username-query="SELECT USERNAME,PASSWORD,IS_ACTIVE FROM USER where USERNAME = ?"
authorities-by-username-query="
SELECT ua.USERNAME, a.AUTHORITY_NAME AS AUTHORITY
FROM USER_AUTHORITIES ua, AUTHORITIES a
WHERE ua.USERNAME = ? and ua.AUTHORITY_ID = a.ID
" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter"
class="org.springframework.security.access.vote.RoleHierarchyVoter">
<property name="rolePrefix" value="" />
<constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy" class="com.datx.dao.MyRoleHierarchyImpl">
<property name="roleHierarchyEntryDaoJdbc" ref="RoleHierarchyEntryDaoJdbc" />
</bean>
</beans>
中使用的所有过滤器
3.当用户使用错误的用户名/密码或尝试访问不允许的页面而不是重定向到login.jsp时,我收到异常。那是为什么?
提前致谢
答案 0 :(得分:4)
首先,我会确保您就此问题咨询了FAQ,以确保您真的想要这样做。正如tom所提到的,通常不建议将此类信息放在数据库中。
就您目前的代码是否/为何工作而言,如果没有更多细节,很难说。例如,您在日志中看到的错误是什么? #2中的问题似乎并不完整。 Spring Security日志说了什么?
如果要坚持这个计划,我会继续完全使用命名空间配置和leverage a BeanPostProcessor (as discussed on the FAQ)来换出FilterInvocationServiceSecurityMetadataSource
。实现可能如下所示:
public class FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor
implements BeanPostProcessor {
private FilterInvocationServiceSecurityMetadataSource metadataSource;
public void setMetadataSource(FilterInvocationServiceSecurityMetadataSource metadataSource) {
this.metadataSource = metadataSource;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if(bean instanceof FilterInvocationSecurityMetadataSource) {
return metadataSource;
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
然后,您的自定义FilterInvocationServiceSecurityMetadataSource
可以在您的Spring配置中与FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor
一起指定。
<bean id="fiMds" class="FilterInvocationServiceSecurityMetadataSourceBeanPostProcessor">
<property name="metadataSource">
<bean id="myFilterInvocationSecurityMetadataSource" class="com.datx.dao.MyFilterSecurityMetadataSource"/>
</property>
</bean>
答案 1 :(得分:2)
感谢Tom和Rob的快速回复。
首先,我完全清楚“将数据库模式存储在数据库中并不是一个好主意”。但是,我们正在尝试动态管理所有内容。所以别无选择。
事实证明,我的代码存在一些小问题。 在这里,我逐一回答每个问题。
我的getAttributes方法工作正常。但是有一种替代方法可以加载url-patterns。 我可以将所有url-patterns及其相应的角色首先加载到HashedMap中。在getAttributes方法中,我可以只查找HasehdMap。 简而言之,就是这样:
问题是以某种方式修剪的!
我试图询问springSecurityFilterChain bean中使用的那些过滤器。
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</security:filter-chain-map>
</bean>
我收到了异常,因为在authenticationProcessingFilter bean中没有这样的属性。 所以我像这样重写它:
<bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
<property name="filterProcessesUrl" value="/j_spring_security_check" />
<property name="usernameParameter" value="j_username" />
<property name="passwordParameter" value="j_password" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>
当然我也必须引入authenticationFailureHandler bean:
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/index.jsp" />
</bean>
现在我没有收到任何例外。
但这里出现了另一个问题:
我无法理解用户名/密码是否不正确或用户名无法访问所请求的页面。
在这两种情况下,都会根据此bean重定向用户:
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp?error=EntryPoint" />
</bean>
为什么要控制这两种情况?