how to do Authentication in Spring security using custom user detail service and angular js 2 as front end - spring

this is my security configuration file
package com.data.topic;
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailService.class)
public class Security extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/node_modules").permitAll()
.antMatchers("/topic/**").hasRole("user")
.and()
.formLogin().loginPage("/sch_signin")
.usernameParameter("username")
.passwordParameter("password")
.successForwardUrl("/")
.failureUrl("/login-error")
.and().csrf().disable();
}
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceBean());
}
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return new CustomUserDetailService();
}
}
i want to know how should i send username and password using angular2 i tried this method on submit
onSubmit(){
let url="http://localhost:8080/sch_signin";
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
console.log(this.user);
this.http.post(url,JSON.stringify(this.user),options);
console.log('data submited');
}
i don't get any error and neither i get authenticated
please help me understand how spring intercept the authentication request

I got the solution after some research. I was posting the form in wrong way the right way to post a form in Angular2.
let url="http://localhost:8080/sch_signin";
let headers = new Headers({'Content-Type':'application/x-www-form-urlencoded'});
let options = new RequestOptions({ headers: headers });
this.http.post(url,body.toString(),options).subscribe((data)=>console.log(data));
First the content type should be application/x-www-form-urlencoded and second you have to send the data in request body so Spring Security can read it.

Related

automate the OAuth2 refresh_token process with SpringBoot 2

I have a SpringBoot2 application, a MainApp as a resource-server, KeyCloak as AuthorizationServer and a maven module, which is related to the MainApp, as a OAuth2LoginClient.
In other words, in MavenModule I have the follow SecurityConfig:
#Configuration
#PropertySource("classpath:idm.properties")
public class Auth0Provider extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers(PROTECTED_URLS).authenticated()
.anyRequest().authenticated()
)
.oauth2Login().redirectionEndpoint().baseUri("/callback*");
http.csrf().disable();
}
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/idmauth/**")
);
}
There is also a controller that intercepts the protected call:
#Value("${oauth.redirectURL}")
private String redirectURL;
#Autowired
private OAuth2AuthorizedClientService clientService;
#RequestMapping(method = RequestMethod.GET, path = "/redirect")
public RedirectView redirectWithUsingRedirectView(OAuth2AuthenticationToken oauthToken, RedirectAttributes attributes) {
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
String token = client.getAccessToken().getTokenValue();
attributes.addAttribute("jwt", token);
return new RedirectView(redirectURL);
}
This return the AccessToken to my frontend. Clearly in my idm.properties file I have the spring.oauth2.client.provider and spring.oauth2.client.registration info.
Now the MainApp is a SpringBoot2 WebApp with this simple SecurityConfig:
#EnableWebSecurity
#Configuration
public class Oauth2RestApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
And in it's application.properties just the line:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://<host>/protocol/openid-connect/certs
All works fine but, when the token expire, the only way I have currently found to refresh my token
is to manually do this HTTP-POST:
POST /auth/realms/<audience>/protocol/openid-connect/token HTTP/1.1
Host: <host>
Content-Type: application/x-www-form-urlencoded
Content-Length: 844
client_id=<my_client_id>
&client_secret=<my_client_secret>
&refresh_token=<refresh_token_previously_obtained>
&grant_type=refresh_token
Is there a better way to do this? Maybe inside the SecurityConfig or with a specific path inside spring.oauth2.x properties?
Note that refreshing an access token is done on the OAuth 2.0 client side.
This is done automatically by Spring Security if you have configured a WebClient to be used when requesting protected resources.
#Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
When you have done so, the expired OAuth2AccessToken will be refreshed (or renewed) if an OAuth2AuthorizedClientProvider is available to perform the authorization.

Spring Security LDAP Auth from Ionic

I developed a Back End with using Java Spring and I added LDAP Authentication with extending WebSecurityConfigurerAdapter. I can get authenticated from POSTMAN but I can't from Ionic.
My Spring side ;
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//TODO: add other endpoints like /events in order to permit un-authenticated users
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login**").anonymous()
.antMatchers("/resources/**").permitAll()
.antMatchers("/assets/**").permitAll()
.antMatchers("/").authenticated()
.antMatchers("/home").authenticated()
.antMatchers("/events/**").authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll()
.and()
.cors()
.and()
.csrf()
.disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource()
.url("ldap://ldap.forumsys.com/dc=example,dc=com")
.and()
.userSearchFilter("(uid={0})")
.userSearchBase("ou=mathematicians")
.userDnPatterns("uid={0}");
}
Login Controller;
#RequestMapping(value = "/")
public String home() throws NamingException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
return "hey, nice! = " + currentPrincipalName;
}
And my Postman Login;
Postman Screenshot
Lastly, my client (ionic) side auth code;
authenticate(event) {
event.preventDefault();
const data = new URLSearchParams();
data.append("username",this.state.username);
data.append("password",this.state.password);
fetch(host + "/login", {
mode: 'no-cors',
method: 'POST',
body: data,
redirect :"follow",
headers: {
'Accept': '*/*'
},
keepalive: true
})
}
But from my Ionic side, I can't get "hey, nice! = euler" response as I get from POSTMAN. I think that I handled with CORS but I didn't figure out whats the problem.
I answered my question.
I added proxy to my package.json and added credentials: "include" to post request header at front end.

obtain request parameter in Spring security Filter

Can someone help in in obtaining request parameter
in WebsecurityConfig Httpsecurity configure method ? I need to extract the request parameter in the below case acr=loa3 that is coming from request
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.maximumSessions(1)
.expiredUrl(this.uiUri + "/expired")
.maxSessionsPreventsLogin(true)
.and()
.invalidSessionUrl(this.uiUri + "/expired")
.and()
.csrf().disable().cors()
.and()
.authorizeRequests()
.antMatchers("/expired").permitAll()
.anyRequest().authenticated()
.and()
//Can some one help me here on how to extract request param coming in the url for example xyz.com/login?acr=loa3 ? I need to send that as acr value before the configureOIDCfilter executes
.addFilterBefore(configureOIDCfilter(http, acrValue),
AbstractPreAuthenticatedProcessingFilter.class)
.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(this.redirectUri));
}
}
#Bean
public OIDCAuthenticationFilter configureOIDCfilter(HttpSecurity http, String acrValue) throws Exception {
OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter();
StaticSingleIssuerService issuerService = new StaticSingleIssuerService();
issuerService.setIssuer(issuerUrl);
filter.setServerConfigurationService(new DynamicServerConfigurationService());
StaticClientConfigurationService clientService = new StaticClientConfigurationService();
RegisteredClient client = new RegisteredClient();
client.setClientId(clientId);
client.setDefaultACRvalues(ImmutableSet.of(acrValue));
return filter;
}
What you showed in your code is configuration. This is done at startup time and cannot catch any request parameters at this time. However, if you want to need to do something by request, you may want to implement a filter as I wrote in my recent blog post.
You could extend from a filter like this:
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public MyAuthenticationFilter(AuthenticationManager authenticationManager) {
this.setAuthenticationManager(authenticationManager);
}
}
Then, try to find what methods you want to override. In example:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
...
}
In the above method you can access the http request parameters.
This filter needs to be added to your configuration as well:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilter(new MyAuthenticationFilter()).
}
A filter will be called for any request and is the only way to receive request parameters (to my knowledge).

Spring Security blocks POST requests despite SecurityConfig

I'm developing a REST API based on Spring Boot (spring-boot-starter-web) where I use Spring Security (spring-security-core e spring-security-config) to protect the different endpoints.
The authentication is done by using a local database that contains users with two different sets of roles: ADMIN andUSER. USER should be able toGET all API endpoints and POST to endpoints based onrouteA. ADMIN should be able to do the same asUSER plus POST andDELETE to endpoints based on `routeB
However the behavior I'm getting is that I can do GET requests to any endpoint but POST requests always return HTTP 403 Forbidden for either type of user - ADMIN and USER - which is not expected what I'm expecting based on my SecurityConfiguration.
Any ideas of what am I missing?
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
logger.info("Using database as the authentication provider.");
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
RouteBController .java
#RestController
public class RouteBController {
static final Logger logger = LoggerFactory.getLogger(RouteBController.class);
public RouteBController() { }
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
public String getStuff() {
return "Got a hello world!";
}
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
public String postStuff() {
return "Posted a hello world!";
}
}
RESTAuthenticationEntryPoint.java
#Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("AppNameHere");
super.afterPropertiesSet();
}
}
BEFORE disabling the CSFR as a way of fixing this issue, please check the resources on Mohd Waseem's answer to better understand why it is important and to have an idea of how it can be properly set up. As RCaetano has said, CSFR is here to help us from attacks and it should not be disabled blindly.
Since this answer still explained the 2 issues on my original questions, I'll leave it as the marked answer to create awareness about possible issues with the CSFT and security routes but don't take it literally.
There were 2 issues in SecurityConfiguration.java that made it misbehave.
Although the 403 Forbidden error message didn't contain any message indication of why it was failing (see example below) it turns out it was due to having CSRF enabled. Disabling it allowed for POST and DELETE requests to be processed.
{
"timestamp": "2018-06-26T09:17:19.672+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/routeB"
}
Also the expression used in antMatched(HttpMethod, String) for RouteB was incorrect because /routeB/* expects it to have something after /. The correct configurtion is /routeB/** since more paths can be present (or not).
The corrected SecurityConfiguration.java is
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors().and().
csrf().disable();
}
Source: StackOverflow em Português
Cross-site request forgery is a web security vulnerability that allows an attacker to induce users to perform actions that they do not
intend to perform.
In your case disabling CSRF protection exposes user to this vulnerability.
Note: If it was pure Rest API with O-Auth protection then CSRF was not
needed. Should I use CSRF protection on Rest API endpoints?
But In your case when user logs in a session is created and cookie is returned in response and without CSRF token Attacker can exploit it and perform CSRF.
It wouldn't be a good idea to disable CSRF instead you can configure your app to return CSRF token in response headers and then use it in all your subsequent state changing calls.
Add this line of code in your SecurityConfiguration.java
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
CsrfTokenResponseHeaderBindingFilter.java
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null) {
response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
}
filterChain.doFilter(request, response);
}
}
Header Response form Server:
Note that we now have CSRF token in the header. This will not change untill the session expires.
Also read: Spring Security’s CSRF protection for REST services: the client side and the server side for better understanding.
It's simple CSRF enabled issue that doesn't allow POST requests. I faced the same problem here's the solution: (Explained)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/form").hasRole("ADMIN") // Specific api method request based on role.
.antMatchers("/home","/basic").permitAll() // permited urls to guest users(without login).
.anyRequest().authenticated()
.and()
.formLogin() // not specified form page to use default login page of spring security
.permitAll()
.and()
.logout().deleteCookies("JSESSIONID") // delete memory of browser after logout
.and()
.rememberMe().key("uniqueAndSecret"); // remember me check box enabled.
http.csrf().disable(); // ADD THIS CODE TO DISABLE CSRF IN PROJECT.**
}
Above code:
http.csrf().disable();
will solve the problem.

HTTP 405 Not Allowed - Spring Boot + Spring Security

I have a simple rest API which works with database. It worked properly until I added the security part. Now it gives HTTP 405 Not Allowed on the POST and DELETE requests. I have no idea why. The GET requests work properly.
So here is the controller class:
#Controller
public class MarkerController {
private Logger logger = Logger.getLogger(MarkerController.class.getName());
#Autowired
private MarkerServiceInterface markerService;
#RequestMapping(value="/markers", method=RequestMethod.GET)
public #ResponseBody List<Marker> getMarkers(#RequestParam(value="city", defaultValue="") String city) {
logger.info("HANDLE GET REQUEST");
return this.markerService.getAllMarkers();
}
#RequestMapping(value="/markers/new", method=RequestMethod.POST)
public #ResponseBody Marker addMarker(#RequestBody Marker marker) {
logger.info("HANDLE POST REQUEST");
this.markerService.addMarker(marker);
return marker;
}
#RequestMapping(value="/markers/delete", method=RequestMethod.DELETE)
public #ResponseBody String deleteMarker(#RequestParam(value="id", defaultValue="") String id) {
logger.info("HANDLE DELETE REQUEST");
if (!id.equals("")) {
logger.info(id);
this.markerService.deleteMarker(Long.parseLong(id));
}
return "";
}
#RequestMapping(value="/admin/map")
public String trafficSpy() {
logger.info("HANDLE MAP");
return "index";
}
#RequestMapping(value="/admin")
public String admin() {
return "admin";
}
#RequestMapping(value="/login")
public String login() {
return "login";
}
}
This is the SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')")
.antMatchers("/markers/**")
.access("hasRole('ROLE_USER')")
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
The DELETE request is called with the following ajax code:
$.ajax({
url: "localhost:8080/markers/delete?id=" + currentMarker.get("id"),
type: 'DELETE',
success: function(result) {
console.log(result);
}
});
And here is the message given in the console:
2015-05-11 15:48:13.671 WARN 8279 --- [nio-8181-exec-6] o.s.web.servlet.PageNotFound : Request method 'DELETE' not supported
These are the headers of the response. I can see that in AlLLOW I have only GET and HEAD. So if I'm right, this means that the method in the controller accepts only GET and HEAD requests.
(Status-Line) HTTP/1.1 405 Method Not Allowed
Server Apache-Coyote/1.1
x-content-type-options nosniff
x-xss-protection 1; mode=block
Cache-Control no-cache, no-store, max-age=0, must-revalidate
Pragma no-cache
Expires 0
X-Frame-Options DENY
Allow GET, HEAD
Content-Type application/json;charset=UTF-8
Transfer-Encoding chunked
Date Mon, 11 May 2015 17:35:31 GMT
In the response I have this exeption:
org.springframework.web.HttpRequestMethodNotSupportedException
Any idea what is causing this problem? How can I allow the POST and DELETE methods?
You forget the csrf-Token.
It's recommended that you add the csrf-Token in the meta-tag. You can read it in the Spring Security Documentation
With this you can do the following:
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
I wanted to leave an additional solution, because although Manu Zi's answer is a correct one, it wasn't immediately clear to me why the first time I came across this issue and found this answer.
The underlying issue was obscured by the immediate problem of the 405 Method Not Allowed.
In my case, there were two factors at play. Firstly, there was no POST method for my AccessDenied controller, which resulted in a 405 when a POST method was denied and redirected to the AccessDenied controller. This was only evident after turning up debug logging on org.springframework.web.
Once that was clear, it was a matter of figuring out why I was getting access denied. All the permissions and roles were correct, but having upgraded from Spring 3 to 4, I found that CSRF protection was enabled by default. It either needs to be disabled, or you have to use Manu Zi's solution.
To disable it, see:
spring security 4 csrf disable via xml
In my case the protocol had to be https instead of http

Resources