Spring Security Struts 1.3 integration without extending GrandAuthority and UserDetails - spring-security

I am new to spring security and wanted to integrate it into existing Struts 1.3 webapp.
I successfully followed this tutorial and it works great when it is that simple.
However in the tutorial user is hardcoded into security.xml:
<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="admin" password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
If I understand correctly to enable custom authentication I need to implement my CustomAuthenticationProvider that extends AuthenticationProvider interface, which has an authenticate method.
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
...
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
However UsernamePasswordAuthenticationToken accepts Collection in its constructor and in our model entities do not extend spring security interaces, such as UserDetails and GrantedAuthority.
So is there a way to authenticate user without extending spring security interfaces in my entities?
EDIT:
I have added customUserDetailService:
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login.do" access="isAnonymous()"/>
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/j_spring_security_check" access="permitAll"/>
<form-login login-page="/login.do"
authentication-failure-url="/login.do?login_error=1"
default-target-url="/index.do"/>
<logout invalidate-session="true"
logout-url="/logout.do"
logout-success-url="/"/>
<csrf />
<remember-me />
<access-denied-handler error-page="/denied"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailService" />
</authentication-manager>
<beans:bean id="customUserDetailService" class="com.demo.service.CustomUserDetailsService"/>
However loadUserByUsername() method is not invoked...

Related

Unable to hit custom AuthenticationProvider using Spring security 5.1.1

I'm using Spring security 5.1.1. I'm trying to create two security entryPoints for my application: one for REST and another for the secured urls of the application. I've created CustomAuthenticationProvider by implementing AuthenticationProvider for the authenticationManager.
I'm following the examples in :
Spring Security for a REST API and
Spring Security – Two Security Realms in one Application
But on the login page, when I enter username and password it doesn't hit the CustomAuthenticationProvider.authenticate() method at all, rather it goes to logout.html.
Below is my xml snippet of http:
<!-- Configuration for API -->
<security:http entry-point-ref="restAuthEntryPoint" pattern="/api/**" use-expressions="true">
<intercept-url pattern="/api/**" access="hasAnyRole('ROLE_DRIVER','ROLE_PARENT') and isAuthenticated()"/>
<intercept-url pattern="/api/driver/**" access="hasRole('ROLE_DRIVER') and isAuthenticated()"/>
<intercept-url pattern="/api/parent/**" access="hasRole('ROLE_PARENT') and isAuthenticated()"/>
<form-login
authentication-success-handler-ref="apiSuccessHandler"
authentication-failure-handler-ref="apiFailureHandler" />
<custom-filter ref="apiAuthenticationFilter" after="BASIC_AUTH_FILTER" />
<logout />
</security:http>
<beans:bean id="apiAuthenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<beans:constructor-arg name="authenticationEntryPoint" ref="restAuthEntryPoint"/>
<beans:constructor-arg name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<beans:bean id="restAuthEntryPoint"
class="com.main.sts.api.security.RestAuthenticationEntryPoint"/>
<beans:bean id="apiSuccessHandler"
class="com.main.sts.api.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
<beans:bean id="apiFailureHandler" class=
"org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>
<!-- Configuration for Rest-API finished-->
<security:http auto-config="true" use-expressions="true" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/school_admin/*"
access="hasAnyRole('ROLE_SCHOOLADMIN','ROLE_GUEST','ROLE_SCHOOLTEACHER','ROLE_PARENT')" />
<form-login login-page="/login" authentication-failure-url="/loginfailed"/>
<!-- <custom-filter before="FORM_LOGIN_FILTER" ref="userAuthenticationProcessingFilter" /> -->
<logout invalidate-session="true" logout-success-url="/logout" />
<access-denied-handler error-page="/404" />
<session-management invalid-session-url="/logout.html">
</session-management>
<sec:headers >
<sec:cache-control />
<sec:hsts/>
</sec:headers>
</security:http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
<beans:bean id="customAuthenticationProvider" class="com.main.sts.util.CustomAuthenticationProvider">
<beans:property name="loginService" ref="loginService" />
</beans:bean>
Even if I commented out the configuration for the REST-api, still I don't get hit to that class.
Here's my CustomAuthenticationProvider:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) {
// **I never hit this class**
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
filter is defined correctly in web.xml:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
springSecurityFilterChain /*
In the login jsp, I've setup form as below:
<form class="form-vertical login-form" name='f' action="<c:url value='j_spring_security_check' />" method="post">
I can't access secured urls, it takes me to the login page; this means - this filter works. But why can't I hit CustomAuthenticationProvider? Why does it go to logout.html???
I've also tried by implementing custom filter (which eventually sets authenticationManager as the property); but still no luck.
I've also checked the log files but nothing in there.
BTW, if I try to access through curl, I get Http status 403 (forbidden). The server understood the request but refuses to authorize it.
curl -i -X POST -d username=admin -d password=Admin123 http://localhost:8080/sts/api/login
Please help me to find out the issue.
Alhamdulillah, finally I've found the issue.The code base which I originally started with was implemented on Spring 2.5. I've upgraded Spring version to 5.1. Basically /j_spring_security_check , j_username and j_password have been deprecated.
Now I've changed my jsp accordingly and it works now.
It's weird that there was no error or warning message.

Spring Rest Authentication with a response

I have setup a security context meant for REST. The configuration is as
<!-- authentication manager and password hashing -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider" />
</authentication-manager>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="passwordEncoder" ref="passwordEncoder" />
</beans:bean>
<beans:bean id="userDetailsService" name="userAuthenticationProvider"
class="com.myapp.auth.AuthenticationUserDetailsGetter" />
<beans:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>
<global-method-security pre-post-annotations="enabled" />
<!-- web services -->
<http use-expressions="true" pattern="/rest/**"
disable-url-rewriting="true" entry-point-ref="restAuthenticationEntryPoint">
<custom-filter ref="restProcessingFilter" position="FORM_LOGIN_FILTER" />
<intercept-url pattern="/rest/login" access="permitAll"/>
<intercept-url pattern="/rest/**" access="isAuthenticated()" />
<logout delete-cookies="JSESSIONID" />
</http>
<beans:bean id="restProcessingFilter" class="com.myapp.auth.RestUsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/rest/login" />
</beans:bean>
And I overrided the UsernamePasswordAuthenticationFilter as
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = null;
String username = request.getParameter("j_username");
String password = request.getParameter("j_password");
boolean valid = authService.authenticate(username, password);
if (valid) {
User user = updateLocalUserInfo(username);
authentication = new UsernamePasswordAuthenticationToken(user,
null, AuthorityUtils.createAuthorityList("USER"));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
return authentication;
}
The above authentication is working fine when I tried it with
RestClient restClient = new RestClient();
String result = restClient.login("hq", "a1234567"); // RestTemplate.postForObject
The only thing left is the result from the authentication post (atm, result is null). How can I configure my security configuration in order to retrieve some result ? A flag or session ID will suffice.
I think best bet here would be AuthenticationSuccessHandler.
As this will only kick in if the authentication was successful.
You can generate some sort of UUID and set that in your response directly. I have used very similar approach for ReST Auth and have not hit any problems yet.
For detailed implementation guide please refer : https://stackoverflow.com/a/23930186/876142
Update for comment #1 :
You can get response just like any normal ReST request.
This is how I am sending back my Token as JSON
String tokenJsonResponse = new ObjectMapper().writeValueAsString(authResponse);
httpResponse.addHeader("Content-Type", "application/json");
httpResponse.getWriter().print(tokenJsonResponse);
Assuming you know how to use RestTemplate, rest is trivial.

Spring security 3 http-basic authentication-success-handler

H i'm using spring security
for form-login i have
<http auto-config="true">
<intercept-url pattern="/pages/**" access="ROLE_USER" />
<form-login authentication-success-handler-ref="authenticationSuccessHandler" login-page="/login.html" default-target-url="/pages/index.html"
always-use-default-target="true" authentication-failure-url="/login.html" />
<logout logout-success-url="/login.html" invalidate-session="true" />
<anonymous enabled='false'/>
</http>
here i can set an authentication-success-handler-ref, how can i add one to my basic authentication:
<http pattern="/REST/**" realm="REALM" entry-point-ref="authenticationEntryPoint">
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic />
<logout logout-url="/REST/logout" success-handler-ref="restLogoutSuccessHandler" />
</http>
i thought abour overriding BasicAuthenticationFilter, but how can i inject my cutom class for <http-basic />
You cannot set an authentication success handler for BASIC authentication. You can, however, extend BasicAuthenticationFilter and override onSuccessfulAuthentication method:
#Component("customBasicAuthFilter")
public class CustomBasicAuthFilter extends BasicAuthenticationFilter {
#Autowired
public CustomBasicAuthFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
protected void onSuccessfulAuthentication(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Authentication authResult) {
// Do what you want here
}
}
Inject it in your security configuration with something like:
<http entry-point-ref="basicEntryPoint">
<custom-filter ref="customBasicAuthFilter" position="BASIC_AUTH_FILTER"/>
</http>
<authentication-manager alias="authenticationManager">
...
</authentication-manager>
Update: Or with Java config instead of XML:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAt(customBasicAuthFilter, BasicAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(basicEntryPoint);
}
As a workaround you can use http-basic in conjuction with form-login:
<http auto-config="true">
...
<http-basic />
<form-login authentication-success-handler-ref="authenticationSuccessHandler" ... />
...
</http>
BasicAuthenticationFilter will work.
EDIT.
If you want set up your overriden version of BasicAuthenticationFilter I think you need to:
Add it to filter chain at BASIC_AUTH_FILTER position as explained here
Set up corresponding BasicAuthenticationEntryPoint entry point via entry-point-ref attribute of http tag.
Instead of using an AuthenticationSuccessHandler you can rely on Spring Security's event mechanism and listen to AuthenticationSuccessEvent by using the ApplicationListener interface:
#Component
public class AuthenticationEventListener implements
ApplicationListener<AuthenticationSuccessEvent>
{
#Override
public void onApplicationEvent (AuthenticationSuccessEvent event) {
// do what you want here
// example: persist event to the database
}
}
See also this answer here: https://stackoverflow.com/a/11384001/474034

Handling both form and HTTP basic authentication with different sources

I already have form login and Basic auth working side by side with the help of a DelegatingAuthenticationEntryPoint.
What I'm trying to do is have users coming thru the login form to be authenticated against criteria "A", and have users coming thru the Basic auth requests to be authenticated against criteria "B".
Some of the application's resources are exposed thru a RESTful service (accessible via Basic auth). Instead of having users enter their own credentials to make a REST service call, they can enter generated key/value pairs for use exclusively with the REST service that can later be revoked by the user or by the app administrator.
I would prefer to share as much of my security-specific beans as possible between the two methods of authentication. I know I will need separate UserDetailsServices as the form login queries my users table, and Basic auth will query my service_credentials table.
What is the correct way to achieve this kind of configuration in Spring Security?
Depending on your app and whether you're using Spring Security 3.1, you might be best to split the configuration into multiple filter chains, each with a separate authentication manager defined:
<http pattern="/rest_api/**" create-session="stateless"
authentication-manager-ref="serviceCredsAuthMgr">
<http-basic />
</http>
<http authentication-manager-ref="mainAuthMgr">
<form-login />
</http>
<authentication-manager id="serviceCredsAuthMgr">
<authentication-provider user-service-ref="serviceCredsUserDetailsSvc" />
</authentication-manager>
<authentication-manager id="mainAuthMgr">
<!-- whatever -->
</authentication-manager>
Instead of the pattern attribute you can also use the request-matcher-ref attribute to specify a RequestMatcher instance which will be used to map incoming requests to a particular filter chain. This has a very simple interface, but can allow you to match based on something other than the URL path, such as the Accept header.
With SpringSecurity (3.2.3.RELEASE) work fine form as well as basic auth:
<http pattern="/resources/**" security="none"/>
<http pattern="/webjars/**" security="none"/>
<http pattern="/rest/**" create-session="stateless" use-expressions="true">
<intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<http-basic />
</http>
<http auto-config="true" use-expressions="true">
<http-basic/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/loginfailed" access="permitAll"/>
<intercept-url pattern="/logout" access="permitAll"/>
<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/**" access="isAuthenticated()"/>
<form-login login-page="/login" default-target-url="/" authentication-failure-url="/loginfailed"/>
<logout logout-success-url="/logout"/>
<remember-me user-service-ref="userService"/>
</http>
<authentication-manager>
<authentication-provider user-service-ref="userService">
<!--
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="SELECT email, password, enabled FROM users WHERE email = ?"
authorities-by-username-query="
SELECT u.email, r.name FROM users u, roles r WHERE u.id = r.user_id and u.email = ?"/>
-->
<!--
<user-service>
<user name="mail#yandex.ru" password="password" authorities="ROLE_USER"/>
<user name="admin#gmail.com" password="admin" authorities="ROLE_ADMIN"/>
</user-service>
-->
</authentication-provider>
</authentication-manager>

Spring Security Remember me with custom authentication provider

I am using GWT with spring security. I have a custom authentication provider where I perform all my authentication. How can I configure the remember me feature without using the UserDetailsService? I am not using LDAP.
My AppliationContext_security.xml
<http auto-config="true" entry-point-ref="UnauthorizedEntryPoint"
create-session="always">
<form-login authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler" />
<logout success-handler-ref="logoutSuccessHandler"
invalidate-session="true" />
<intercept-url pattern="/**/myapp.rpc" access="ROLE_USER" />
<custom-filter before="CONCURRENT_SESSION_FILTER" ref="XSRFAttackFilter" />
</http>
<authentication-manager>
<authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>
In my custom authentication provider,
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
boolean response = loginmanager.authenticateUser(username, password,
((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest().getSession());
if (!response) {
throw new BadCredentialsException(
"Invalid Credentials.");
}
Authentication authentication = ...
authentication.setAuthenticated(true);
return authentication;
}
Any help will be greatly appreciated.
You will need to create a custom UserDetailsService that gets the username/password from the same place that your loginmanager is reading it from. See the source for TokenBasedRememberMeServices.processAutoLoginCookie() to see how it's being used.

Resources