Not able to get csrf token with spring security 3.2.7 - rest

I am using Spring Security 3.2.7 with spring boot 1.2.3. I am building Rest application and implementing spring security with java config in which CSRF is on by default and I know I can disable it in my overridden configure method with "http.csrf().disable()". But suppose I don't want to disable it, but I need the CSRF token. When i hit the url
localhost:8080/myproject/url
with POST request it gives
{
"timestamp": 1431682924618,
"status": 403,
"error": "Forbidden",
"message": "Expected CSRF token not found. Has your session expired?",
"path": "/myproject/user"
}
So how can i hit the same url with successful result without disabling the csrf.
My SecurityConfig file is:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private DataSource dataSource;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// In memory authentication
/*auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");*/
auth.userDetailsService(customUserDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable() //Disable csrf security
http
.authorizeRequests()
.antMatchers("/myproject/user/signup").permitAll()
.antMatchers("/myproject/**").hasRole("ADMIN")
.and().httpBasic();
}
}

Related

Spring Boot application with Actuator

I have an SpringBoot app. 2.1.3.RELEASE securized by JWT, I want to add an actuator. I added this dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
this is my configFile:
#Profile("api")
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(ApiWebSecurityConfig.class);
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UserSecurityService userSecurityService;
#Value("${jwt.header}")
private String tokenHeader;
#Value("${server.servlet.context-path}")
private String serverContextPath;
/** The encryption SALT. */
private static final String SALT = "fd&eekj§sfs23#$1*(_)nof";
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userSecurityService)
.passwordEncoder(passwordEncoder());
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12, new SecureRandom(SALT.getBytes()));
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// Un-secure H2 Database
.antMatchers("/h2-console/**/**").permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity
.headers()
.frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
.cacheControl();
}
#Override
public void configure(WebSecurity web) throws Exception {
// AuthenticationTokenFilter will ignore the below paths
web
.ignoring()
.antMatchers(
HttpMethod.POST,
"/auth"
)
.antMatchers(
HttpMethod.GET,
"/actuator"
)
.antMatchers(
HttpMethod.POST,
"/reg"
);
}
}
but when I access in the postman to http://127.0.0.1:8080/myApp/actuator/
I got a
{
"timestamp": "2019-03-21T16:39:47.877+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/myApp/actuator/"
}
and HTTP Status 404 – Not Found
when accessing http://127.0.0.1:8080/actuator/
By default the URL is:
http://localhost:8080/actuator
try to change your config from
.antMatchers(
HttpMethod.GET,
"/actuator"
)
to
.antMatchers(
HttpMethod.GET,
"/actuator/**"
)
The Spring boot actuator contains multiple endpoints which include health, metrics, etc.
The endpoints are accessed as follows;
http://{baseUrl}/autuator/health
http://{baseUrl}/autuator/metrics
so get all the endpoints - http://{baseUrl}/autuator/** [GET Request]
so to permit access to this endpoint in your security configuration, change your config from.
.antMatchers(
HttpMethod.GET,
"/actuator"
)
to
.antMatchers(
HttpMethod.GET,
"/actuator/**"
)

Connecting OAuth2 resource server with authentication server

I'm trying to make a sample OAuth2 Spring authorization and resource server. My intention is to implement two separate applications - one representing authorization server ant the other representing resource server. Since I'm quite a beginner in Spring Security, I guess I need some guidance to complete my task.
I already managed to implement a simple authorization server using in-memory token store (app named "OAuth").
AuthServerOAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds(RESOURCE_ID)
.secret("test")
.accessTokenValiditySeconds(300).//invalid after 5 minutes.
refreshTokenValiditySeconds(600);//refresh after 10 minutes.
// #formatter:on
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).userApprovalHandler(handler).authenticationManager(authManager);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
OAuth2SecurityConfig.java
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfig.class);
#Autowired
private ClientDetailsService clientService;
#Autowired
private DataSource dataSource;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.inMemoryAuthentication()
.withUser("javabycode").password("123456").roles("USER")
.and()
.withUser("admin").password("admin123").roles("ADMIN");
// #formatter:on
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
// #formatter:on
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
handler.setClientDetailsService(clientService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
Accessing http://localhost:9081/OAuth/oauth/token?grant_type=password&username=admin&password=admin123 returns token as expected, so I'm guessing that authorization server is configured ok.
Now there's a resource server part (app named "RestTest"). I've managed to find some examples using RemoteTokenServices to access token service that resides in another app. So here's my resource server so far.
OAuth2ResourceConfig.java
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "myResource";
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.
anonymous().disable()
.requestMatchers().antMatchers("/v1/**")
.and().authorizeRequests()
.antMatchers("/v1/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
// #formatter:on
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws
Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:9081/OAuth/oauth/check_token/");
tokenService.setClientId("test");
tokenService.setClientSecret("test");
return tokenService;
}
}
I'm trying to secure my REST API (http://localhost:9081/RestTest/v1/foobar) so I believe that configuration above is correct, right? Problem is that when I access v1/foobar endpoint (via Postman) it's accessible without any authentication. So I think I'm simply missing some part of configuration, but I can't figure it out how to connect to authorization server correctly. One more thing to mention - I'm not using Spring Boot!
I'd really appreciate some guidance to make my sample work. Thanks!
EDIT1: I've added resourceId to both authentication and resource server - no luck. Is resourceId even mandatory?
You should add RESOURCE_ID both in ResourceServer and AuthorizationServer in a way that, (you updated your question though with that snippet)
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}
And in your auth server
.scopes("read", "write", "trust").resourceIds(RESOURCE_ID)
Add a springSecurityFilterChain as you missing that in web.xml that you already said in comment
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
From spring docs:
It creates a Servlet Filter known as the springSecurityFilterChain which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application.

Spring Boot security for rest

I know there are a lot of topics on that but is there any way just modify the normal spring security to work with json objects.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //za pre i post authorize v servisa
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
//Koi service shte polzvame
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.antMatchers("/", "/user/register", "/css/**", "/js/**").permitAll()
.antMatchers("/user/user").access("hasRole('USER') or hasRole('ADMIN')")
.antMatchers("/user/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/user/login").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and()
.rememberMe().rememberMeCookieName("RememberMeFromLecture")
.rememberMeParameter("remember")
.key("golqmaTaina")
.and()
.logout().logoutSuccessUrl("/user/login?logout").logoutRequestMatcher(new AntPathRequestMatcher("/signout")).permitAll()
.and()
.exceptionHandling().accessDeniedPage("/user/unauthorized")
.and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(this.userService).passwordEncoder(getBCryptPasswordEncoder());
}
#Bean
public BCryptPasswordEncoder getBCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
}
This is my config file and it works perfectly without rest, but my problem is just want to make the login page to work with rest that's all. If it's configed like this, my login is been done automatically I can't even set a break point inside my controllers. It works, but i want to make it work with rest.
I created a sample application (https://github.com/manishsingh27/TokenBasedAuth) and it is based on REST for authentication.
Client application is based on AngularJS and it has login page, files are here - https://github.com/manishsingh27/TokenBasedAuth/tree/main/authz/src/main/resources/static.
And REST APIs are present here - https://github.com/manishsingh27/TokenBasedAuth/blob/main/authz/src/main/java/com/adms/authz/self/user/controller/UsersController.java.
Config file is here -https://github.com/manishsingh27/TokenBasedAuth/blob/main/authz/src/main/java/com/adms/authz/config/SecurityConfiguration.java
You need to use the #EnableResourceServer annotation to secure the Rest APIs.

Spring + Oauth2: how to refresh access token

I am building rest web services with Spring Boot. Authentication is implemented using Spring Security and OAuth2. Users are authenticated against LDAP server. Here is my websecurityconfig
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(
SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.antMatchers("/ristore/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public RestAuthenticationSuccessHandler mySuccessHandler(){
return new RestAuthenticationSuccessHandler();
}
#Bean
public SimpleUrlAuthenticationFailureHandler myFailureHandler(){
return new SimpleUrlAuthenticationFailureHandler();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = getSource();
auth
.ldapAuthentication()
.userDnPatterns("cn={0},ou=institution,ou=people")
.groupSearchBase("ou=groups")
.contextSource(contextSource);
}
}
Additional config is done in authserverconfig including clientdetailservice.
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new InMemoryTokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ristoreclient")
.scopes("read")
.authorizedGrantTypes("password", "refresh_token", "client_credentials")
.secret("ristoresecret")
.accessTokenValiditySeconds(60);
}
}
It works for initial login. However when I try to get a new access token with refresh token when the old expires, I got the error "UserDetailsService is required". After searching for answers online, I found this post with similar problem: spring-security-oauth2 2.0.7 refresh token UserDetailsService Configuration. Basically the solution there was to create a custom LdapUserDetailsService. Thing is it was set up in xml config instead of java. Besides, it is not clear how and where this class is injected. In this other case, userdetailservice instance is added in auth server endpoint config instead. This article does not provide the implementation of this class.
The idea of having a userdetailservice, in my opinion, is to look up and see if this user is still active before issuing a new access token. What is contradictory is that the request of getting a refresh_token for oauth2 only consists of the following information which does not include username/password.
client_id=clientid
client_secret=clientsecret
refresh_token=1/6BMfW9j53gdGImsiyUH5kU5RsR4zwI9lUVX-tqf8JXQ&
grant_type=refresh_token
OAuth2 for a Spring REST uses Zuul proxy as a middle layer between front end and web api to handle refresh token which makes the configuration more complex. How should I implement a userdetailsservice for oauth2 in Spring Boot and where should I inject it?

Spring Security simulataneous usage of multiple PasswordEncoders

I have spring app with simple configuration of Spring Security using bcrypt (default parameters) and the test works fine, however i want to plan that this application gives the ability to the administrator or the user to change password and select authentication parameters to be used such as:
1)bcrypt (BCryptPasswordEncoder)
2)hash function such as sha (StandardPasswordEncoder),
So the question is how to change the following class (or the AuthenticationManagerBuilder specifically ) in order to reflect that some users could have they password stored as a sha hash but other as bcrypt, Taking into account that the database table already have a column that specify what kind of hash is being stored in the password column i.e. bcrypt or sha.
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService iUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// .csrf().disable()
// .headers().disable()
.headers()
.contentTypeOptions()
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.frameOptions()
.addHeaderWriter(new StaticHeadersWriter("X-Content-Security-Policy","script-src 'self"))
.addHeaderWriter(new StaticHeadersWriter("Content-Security-Policy","script-src 'self'"))
.addHeaderWriter(new StaticHeadersWriter("X-WebKit-CSP","default-src 'self'"))
.and()
.exceptionHandling()
.accessDeniedHandler(syncAccessDeniedHandler())
.and()
.authorizeRequests()
.antMatchers( "/register",
"/static/**",
"/h2/**",
"/resources/**",
"/static/css/**",
"/static/img/**" ,
"/static/js/**",
"/static/pdf/**",
"/resources/static/css/**",
"/resources/static/img/**" ,
"/resources/static/js/**",
"/resources/static/pdf/**",
"/pdf/**",
"/css/**",
"/js/**",
"/img/**"
).permitAll()
.antMatchers("/admin/dashboard/**").hasAnyRole("STUDENT", "ADMIN")
.antMatchers("/admin/network/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(iUserDetailsService).passwordEncoder(pwEncoder());
}
#Bean
public BCryptPasswordEncoder pwEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SyncAccessDeniedHandler syncAccessDeniedHandler() {
String uri = "/403";
return new SyncAccessDeniedHandler(uri);
}
}
maybe you can use DelegatingPasswordEncoder from spring 5.0 or extend it to create your own version.
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/password/DelegatingPasswordEncoder.html