spring security authorization only - java

I am trying to develop a User management tool using Waffle to perform windows authentication with Spring Security. Unfortunately, The only thing that provides me is the authentication part.
I would like to assign a Role to a particular user session in order to limit a users privileges. Each username is stored in a database along with their associated role. How can I get Spring Security to query my database and load the associated role to the session, so that I can use the #PreAuthorize(hasRole(role)) annotation in my controller to restrict access to certain actions?
Edit:
Thanks for your answer but I don't think thats quite what I am looking for.
So I have made some progress(I think). For my Authentication provider I have created my own custom GrantedAuthorityFactory as a property of my waffleSpringAuthenticationProvider as follows:
<bean id="waffleSpringAuthenticationProvider" class="waffle.spring.WindowsAuthenticationProvider">
<property name="AllowGuestLogin" value="false" />
<property name="PrincipalFormat" value="fqn" />
<property name="RoleFormat" value="both" />
<property name="AuthProvider" ref="waffleWindowsAuthProvider" />
<property name="grantedAuthorityFactory" ref ="simpleGrantedAuthorityFactory"/>
<!-- -->
</bean>
The grantedAuthorityFactory code is as follows:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import waffle.spring.GrantedAuthorityFactory;
import waffle.windows.auth.WindowsAccount;
public class SimpleGrantedAuthorityFactory implements GrantedAuthorityFactory{
private final String PREFIX;
private final boolean CONVERT_TO_UPPER_CASE;
#Autowired
#Qualifier(value = "jdbcRoleDao")
private JdbcRoleDao jdbcRoleDao;
public SimpleGrantedAuthorityFactory(String prefix, boolean convertToUpperCase)
{
PREFIX = prefix;
CONVERT_TO_UPPER_CASE = convertToUpperCase;
}
#Override
public GrantedAuthority createGrantedAuthority(WindowsAccount windowsAccount) {
System.out.println("Username: "+windowsAccount.getFqn());
String grantedAuthorityString = windowsAccount.getFqn();
String grantedAuthority = jdbcRoleDao.getRole(grantedAuthorityString);
return new SimpleGrantedAuthority(PREFIX+grantedAuthority);
}
}
Now when I run the program and try to log in, the login fails. When I remove my custom factory property from the config file, the login is completed successfully with no assigned roles. I'm not sure if this is important, but windowsAccount.getFqn() is not returning the correct username that I enter on my login form.
Is there something I'm missing from my factory class?

You have two options:
Configure JdbcDaoImpl as your UserDetailsService if you use provided DB schema
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="yourDataSource">
</authentication-provider>
</authentication-manager>
Write and configure your own UserDetailsService if you use custom DB schema.
<authentication-manager>
<authentication-provider user-service-ref="idOfYourCustomUserDetailsService" />
</authentication-manager>

Related

Spring - SaltSource is null

I'm using Spring Security and Spring Boot. I'm using too a UserDetails and UserDetailsService.
When execution passed into DaoAuthenticationProvider class and the method additionalAuthenticationChecks(...), the SaltSource is null.
Do you know why ? Perhaps a configuration is needed ?
Thx.
You have to set the salt source. See for example this question as a possible way to do that: https://stackoverflow.com/a/26149525/185031
A SaltSource is needed to be configured. You can use one of its available implementations: SystemWideSaltSource or ReflectionSaltSource.
If you choose ReflectionSaltSource, you have to specify which property of UserDetails will be used for the salt. You may use any existing property of your UserDetails implementation (i.e username) or a specific salt field.
From security perspective, you should better consider a securely generated random salt. In that case, BCryptPasswordEncoder does the job for you (generating salt + storing it with the password).
Here are samples configurations found here.
In XML:
<bean id="authProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="customUserService" />
<property name="passwordEncoder" ref="encoder" />
</bean>
<bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
In java:
auth.jdbcAuthentication().dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery("sql...")
.authoritiesByUsernameQuery("sql...");

Spring security changing sessionid

I'm using spring security 3.1.4 and I have the following problem:
I implemented my custom SavedRequestAwareAuthenticationSuccessHandler and I implemented a cache SessionRegistry.
The problem is that the session id that I get in the SessionRegistry.registerNewSession is different then the on i get in SavedRequestAwareAuthenticationSuccessHandler .onAuthenticationSuccess
The session registery is called first.
what is the correct one? How can I get the same in both?
Is there a way that the custom SessionRegistry.registerNewSession will take the spring security session id?
Just a mere guess. But it sounds like the following issue.
In Spring security by default there is a feature enabled called session fixation protection.
It migrates the session to a new ID for a security reason.
Imagine somebody supplies you an url with an existing session ID via email, you click the link and login.
Now the person who can supplied you the url, can simply hack your account by using the supplied session id.
If you want to disable it, you can do so by putting the following line in your spring security configuration. However be aware of the risk.
<http .. >
...
<session-management session-fixation-protection="none">
...
</http>
The way I solved it was with the below steps:
Added a new bean for SessionFixationProtectionStrategy
<bean id="sessionFixationProtectionStrategy" class="org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy" />
Added the new bean in # 1 as a property to the AuthenticationFilter bean
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
...
<property name="sessionAuthenticationStrategy" ref="sessionFixationProtectionStrategy" />
...
</bean>
Added a session management configuration with session-fixation-protection property in the <security:http> tag
<security:http>
<security:headers />
<security:csrf disabled="true"/>
...
<security:session-management session-fixation-protection="changeSessionId" />
</security:http>
Per Spring Security doco, SessionFixationProtectionStrategy is used as SessionAuthenticationStrategy for Java EE Servlet API implementations pre 3.1 - https://docs.spring.io/spring-security/site/docs/4.2.13.BUILD-SNAPSHOT/apidocs/org/springframework/security/web/authentication/session/SessionFixationProtectionStrategy.html, whilst from Servlet API 3.1, ChangeSessionIdAuthenticationStrategy should be used.

Using user login information from a Seam webapp in a Spring webapp

I have a webapp built on the Seam framework and I am currently in the process of replacing this with a new webapp built on Spring. Our users' login information is stored in a database and encrypted using a salted hash. These are current generated in the Seam app with:
PasswordHash.instance().generateSaltedHash(plainTextPassword, saltPhrase, "SHA")
The problem I'm having is that the new Spring app has to use the same logins and I'm having trouble replicating the password hashing. I currently have this SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDAO userDAO;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
for (AdminUser u : userDAO.getAdminUsers()) {
System.out.println("Granting access to login: " + u.getLogin()
+ " password: " + u.getPassword());
auth.inMemoryAuthentication().withUser(u.getLogin())
.password(u.getPassword()).roles("USER");
}
}
}
Any help would be appreciated. Just add a comment if there's any extra code you need to see.
The way to configure spring security to read users from a given table is via the DaoAuthenticationProvider, see here the official docs.
There is support for defining how to hashing and salting should be done, bellow is how the XML looks like:
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="saltSource" ref="saltSource"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
In Java configuration, in the configure method it's possible to instantiate a DaoAuthenticationProvider, configure it and pass it to the AuthenticationManagerBuilder using the API auth.authenticationProvider(...).

using form-login in a Multi-tenant webapp using Spring MVC and Spring Security

I created a small and simple webapp using Spring Security and SpringMVC and I'm trying to convert it to be a multi-tenant application.
The concept I want is to re-use actual JSPs I have and alter their contents based on configuration which I determine based on the path of the URL.
Example:
Customer #1 (abc) - URL: http://mydomain.com/abc/login.html
Customer #2 (xyz) - URL: http://mydomain.com/xyz/login.html
So the name of the "tenant" is a prefix to the page's path.
I modified my controller to be like this:
#Controller
#RequestMapping("/{customer:[a-zA-Z0-9]+}/login.htm")
public class LoginController
{
private static final Logger logger = Logger.getLogger(LoginController.class);
#RequestMapping
#ReadOnlyRequest
public String login(#PathVariable("customer") String customer, HttpServletRequest request)
{
// Do some 'customer' related actions here
return "login"; // Map to the 'login.jsp' view
}
}
My view resolver configuration is:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Until now, I had the following form-login configuration:
<form-login
login-page="/login.htm"
authentication-failure-url="/login.htm?error=true"
login-processing-url="/login_process"
default-target-url="/index.jsp"
always-use-default-target="true"
/>
But I do not know how to convert it to support my changes.
Is there a way to convert it to something like:
<form-login
login-page="/${customer}/login.htm"
authentication-failure-url="/${customer}/login.htm?error=true"
login-processing-url="/${customer}/login_process"
default-target-url="/index.jsp"
always-use-default-target="true"
/>
One possible idea is to use URL rewriting instead of manual handling of tenant identifiers. This way you can completely decouple tenant handling logic from your code, for example, as follows:
You define an inbound rewriting rule that converts /abc/login.html to /login.html and saves tenant identifier as a request attribute.
You define an outbound rule that appends the current tenant identifier to URLs being written into response. I think Spring Security should respect such a rule when sending redirects (if it doesn't, you can define a custom RedirectStrategy).
Though I have not tested this idea and cannot be sure that it would work.
See also:
OCPSoft Rewrite
UrlRewriteFilter

How do I check method level spring security

I have implemented spring security in controller method.
Below is my spring security.xml
-->
<!-- URL pattern based security -->
<security:http auto-config="false" entry-point-ref="authenticationEntryPoint"
use-expressions="true">
<custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url access="hasAnyRole('ROLE_ADMIN','ROLE_USER')" pattern="/common/admin/**" />
<security:intercept-url pattern="/common/accounting/**" access="hasRole('ROLE_USER')" />
<security:logout logout-url="/j_spring_security_logout" invalidate-session="true" logout-success-url="/login"/>
</security:http>
Below is my controller
#Secured({"ROLE_ADMIN"})
#RequestMapping(value = "/common/admin/addAdmin", method = RequestMethod.GET)
public String add(ModelMap map) {
map.addAttribute(new Administrator());
return "/common/admin/addAdmin";
}
#Secured({"ROLE_ADMIN"})
#RequestMapping(value = "/common/admin/addAdmin", method = RequestMethod.POST)
public String processadd(
#ModelAttribute("administrator") Administrator administrator) {
this.administratorManager.addAdmin(administrator);
return "/common/admin/success";
}
I allow the url /common/admin/** for both admin and user role. But i do some restriction in the admin controller. when user is go in to /common/admin/* as a user role, he can but he can also go in to method that is only for admin role only.
How can I solve it?
Thanks!
You already have added the #Secured annotation.
But you need to enable it:
<!-- secured-annotations = (#Secured("ROLE_ADMIN")) -->
<!-- jsr250-annotations = (#RunAs #RolesAllowed #PermitAll #DenyAll #DeclareRoles) -->
<!-- pre-post-annotations = #PreAuthorized("hasAuthority('ROLE_ADMIN')") -->
<global-method-security
secured-annotations="enabled"
jsr250-annotations="disabled"
pre-post-annotations="disabled">
</global-method-security>
#Secured can take a single or several roles.
#Secured("ROLE_USER")
#Secured({"ROLE_USER", "ROLE_ADMIN"}) //grand access if the user has one of this roles
BWT: From Spring Security 3 Book (http://www.springsecuritybook.com/):
The #Secured annotation is functionallz and syntactiallz the same as #RollesAllowed ... As #Secured functions the same as the JSR standard #RollesAllowed there's not reallz a compelling reason to use it (#Secured) in in new code...
(do not forgett to enable it jsr250-annotations="enabled")
I believe you could have multiple roles defined with #Secured annotation . Is this what you need?
If this is the case , try #RolesAllowed
Check this FAQ. Make sure the global-method-security element is in the web context file if you want to apply security to Spring MVC controllers.
Also, you may need to enable class proxying, using
<global-method-security secured-annotations="enabled" proxy-target-class="true" />
if your controller implements an interface and the method you are securing is not part of that interface (you'll also need cglib as an additional dependency in your app for this).
IF you want to use annotations, better put the following in servlet.xml. There is no point of enabling the annotations n spring-security-xml as it will not take any effect.
Putting above in servlet.xml will do the trick.

Categories