I’ve been working on a spring security recently and I need to know how I can define intercept-url (in Spring Security) dynamically using a Database.
I’ve already dug deep the whole internet and I could not find any unique (and of course useful) tutorial in this area.
So here is what I did:
First I implemented FilterInvocationSecurityMetadataSource Abstract class:
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;
}
and I set my security.xml as:
<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>
There are some problems that I cannot find out:
1. I’ve inserted some pairs like <“URL” , “ROLE”> into URL_ACCESS database. But I’m not sure whether getAttributes method is working fine or not
2. Do I have to implement all those filters that I used in
3. I’m receiving exception when a user uses a wrong username/password or tries to access not-permitted-pages, instead of being redirected to login.jsp. Why is that?
Thanks in advance
Thank you both Tom and Rob for your quick replies.
First of all, I’m totally aware of “storing url-patterns in database is not a good idea”. However we are trying to manage everything dynamically. So there are no other choices.
As it turned out, there were some minor problems with my code.
Here I answer each of my questions one by one.
My getAttributes method is working just fine. But there is an alternative to load url-patterns.
I could load all url-patterns and their corresponding roles first into a HashedMap seperately. And in getAttributes method I could just look up the HasehdMap instead.
In brief, it’d be like this:
And in my getAttributes method I use this url-access thing.
The question is somehow trimmed!
I was trying to ask about those filters used in springSecurityFilterChain bean.
I was receiving exceptions because in authenticationProcessingFilter bean there were no such properties.
So I re-write it like this:
and of course I had to introduce authenticationFailureHandler bean this as well:
Now I’m not receiving any exceptions.
But here arises another question:
I cannot understand whether username/password is incorrect or the username does not have access to the requested page.
In both cases, the user is redirected according to this bean:
Why is it controlling both cases?