I'm trying to secure my REST Api with authorization grant flow with spring.
I could obtain (with Postman) an access token, i put the authorization into the header with Bearer, but i could not access resources because Spring Security tell me that:
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository[186] - HttpSession returned null object for SPRING_SECURITY_CONTEXT
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository[116] - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#6e24700e. A new one will be created.
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.h.writers.HstsHeaderWriter[130] - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#3e385c64
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.security.web.FilterChainProxy[325] - /api/user at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter[100] - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9057bc48: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: B1FF11055AA4F347AB8AA7B6E467D93F; Granted Authorities: ROLE_ANONYMOUS'
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor[219] - Secure object: FilterInvocation: URL: /api/user; Attributes: [authenticated]
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor[348] - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9057bc48: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: B1FF11055AA4F347AB8AA7B6E467D93F; Granted Authorities: ROLE_ANONYMOUS
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.access.vote.AffirmativeBased[66] - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#53b3549c, returned: -1
2017-04-06 17:36:33 [http-nio-8080-exec-9] DEBUG o.s.s.w.a.ExceptionTranslationFilter[173] - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
So basically, after having obtained the access token, if i use it, i will be an Anonymous User, basically because SPRING_SECURITY_CONTEXT is null...
This is my ResourceServer configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
private final Logger logger = LoggerFactory.getLogger(ResourceServerConfig.class);
#Autowired
DataSource dataSource;
#Override
public void configure(HttpSecurity http) throws Exception {
logger.debug("Api security configured");
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().access("hasRole('USER')")
.and().exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and().httpBasic();
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore());
}
}
This one is the authentication Server
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authManager);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
I'm trying to access /api/user with this auth Bearer 77a226bf-74a4-4a89-b2a6-e130c215566b which came from the auth server token request after logging in with the user...
What's wrong?
I've had exactly the same issue after updating spring boot from 1.4 to 1.5. The problem was solved by disabling boot's autoconfiguration black magic.
#EnableAutoConfiguration(exclude = {OAuth2AutoConfiguration.class})
I believe they've added some new ~~bug~~ feature which breaks old apps config.
I think User Role is not getting fetched from the database.
Have to define the role column?
This will help you:
https://dzone.com/articles/spring-security-4-authenticate-and-authorize-users
Related
My Spring Boot application is returning a 403 status code (forbidden) when I call a GET request in my AdminControllor
I am trying to blacklist URLs which have "/admin/**" except for those users who have the authority "ADMIN" in my database.
I have disabled csrf which seems to be the most common issue for 403 forbidden.
The problem seems to relate (see debug logs) to this:
Failed to authorize filter invocation [GET /admin/allusers] with attributes [hasAuthority('ADMIN')]
Logs
2022-09-08 13:56:20.018 DEBUG 21328 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /admin/allusers
2022-09-08 13:56:20.026 DEBUG 21328 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-09-08 13:56:20.026 DEBUG 21328 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2022-09-08 13:56:20.042 DEBUG 21328 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /admin/allusers] with attributes [hasAuthority('ADMIN')]
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] o.s.s.w.s.HttpSessionRequestCache : Saved request http://localhost:8080/admin/allusers to session
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] o.s.s.w.a.Http403ForbiddenEntryPoint : Pre-authenticated entry point called. Rejecting access
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /error
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2022-09-08 13:56:20.090 DEBUG 21328 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Secured GET /error
2022-09-08 13:56:20.190 DEBUG 21328 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
2022-09-08 13:56:20.198 DEBUG 21328 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : Did not store anonymous SecurityContext
My Security Config class
package com.example.spring_security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.mvcMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().permitAll();
return http.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
My UserDetails
package com.example.spring_security.security;
import com.example.spring_security.entities.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.stream.Collectors;
public class SecurityUser implements UserDetails {
private final User user;
public SecurityUser(User user){
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities()
.stream()
.map(SecurityAuthority::new)
.collect(Collectors.toList());
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
My UserDetailsService
package com.example.spring_security.services;
import com.example.spring_security.repositories.UserRepository;
import com.example.spring_security.security.SecurityUser;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
#Service
public class JpaUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public JpaUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) {
var u = userRepository.findUserByUsername(username);
return u.map(SecurityUser::new)
.orElseThrow(() -> new UsernameNotFoundException("Username not found " + username));
}
}
My GrantedAuthority
package com.example.spring_security.security;
import com.example.spring_security.entities.Authority;
import org.springframework.security.core.GrantedAuthority;
public class SecurityAuthority implements GrantedAuthority {
private final Authority authority;
public SecurityAuthority(Authority authority) {
this.authority = authority;
}
#Override
public String getAuthority() {
return authority.getName();
}
}
I managed to get this working by adding formLogin() in my security configuration class. If omitted from Spring Security then there is no configuration for the authentication filter. See here.
I'm trying to get some Role-based authorization working on spring, but I have trouble with 403 responses on POST\PUT\DELETE requests. I've been looking for solutions elsewhere but provided solutions with disabling csrf do not solve the issue.
This is my HTTP config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/**").hasAnyRole("ROLE_TRAINER", "ROLE_ADMIN")
.antMatchers(HttpMethod.PATCH, "/user/**").hasAnyRole("ROLE_TRAINER", "ROLE_ADMIN")
.antMatchers(HttpMethod.PUT, "/**").hasAnyRole("ROLE_TRAINER", "ROLE_ADMIN")
.antMatchers(HttpMethod.POST, "/**").hasAnyRole("ROLE_TRAINER", "ROLE_ADMIN")
.antMatchers(HttpMethod.DELETE,"/**").hasAnyRole("ROLE_TRAINER", "ROLE_ADMIN")
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin().permitAll()
.and()
.logout().permitAll();
}
When I debug my code, only GET requests make my program go through the UserDetails objects to provide the Roles collection (as defined below)
public class BrevisFitUser extends User implements UserDetails {
public BrevisFitUser(final User user) {
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName().toUpperCase()))
.collect(Collectors.toList());
}
On a PUT request for instance, this is the log I get:
2019-11-20 13:28:47.560 DEBUG 15456 --- [nio-8080-exec-5] o.a.coyote.http11.Http11InputBuffer : Received [PUT /exercise/strap/3 HTTP/1.1
Content-Type: application/json
Authorization: Basic c2ltb24ua25lejpzaW1vbi5rbmV6
User-Agent: PostmanRuntime/7.19.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 7a829877-6de4-4746-8e27-63677f0160d2
Host: localhost:8080
Accept-Encoding: gzip, deflate
Content-Length: 75
Cookie: JSESSIONID=45F53EE556C13E672E6ECFD5865B5FD6
Connection: keep-alive
{
"unitLength": 29,
"name": "Yellow strap home marked smaller"
}]
2019-11-20 13:28:47.560 DEBUG 15456 --- [nio-8080-exec-5] o.a.t.util.http.Rfc6265CookieProcessor : Cookies: Parsing b[]: JSESSIONID=45F53EE556C13E672E6ECFD5865B5FD6
2019-11-20 13:28:47.561 DEBUG 15456 --- [nio-8080-exec-5] o.a.catalina.connector.CoyoteAdapter : Requested cookie session id is 45F53EE556C13E672E6ECFD5865B5FD6
2019-11-20 13:28:47.561 DEBUG 15456 --- [nio-8080-exec-5] o.a.c.authenticator.AuthenticatorBase : Security checking request PUT /exercise/strap/3
2019-11-20 13:28:47.561 DEBUG 15456 --- [nio-8080-exec-5] org.apache.catalina.realm.RealmBase : No applicable constraints defined
2019-11-20 13:28:47.562 DEBUG 15456 --- [nio-8080-exec-5] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
2019-11-20 13:28:47.563 DEBUG 15456 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /exercise/strap/3 at position 1 of 15 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2019-11-20 13:28:47.563 DEBUG 15456 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /exercise/strap/3 at position 2 of 15 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2019-11-20 13:28:47.563 DEBUG 15456 --- [nio-8080-exec-5] w.c.HttpSessionSecurityContextRepository : Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl#e2265dfb: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#e2265dfb: Principal: com.brevisfit.api.model.user.User[ iduser=1 ]; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#fffde5d4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 45F53EE556C13E672E6ECFD5865B5FD6; Granted Authorities: ROLE_ADMIN, ROLE_TRAINER'
2019-11-20 13:28:47.563 DEBUG 15456 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /exercise/strap/3 at position 3 of 15 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2019-11-20 13:28:47.563 DEBUG 15456 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /exercise/strap/3 at position 4 of 15 in additional filter chain; firing Filter: 'CsrfFilter'
2019-11-20 13:28:47.564 DEBUG 15456 --- [nio-8080-exec-5] org.apache.tomcat.util.http.Parameters : Set encoding to UTF-8
2019-11-20 13:28:47.564 DEBUG 15456 --- [nio-8080-exec-5] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:8080/exercise/strap/3
2019-11-20 13:28:47.564 DEBUG 15456 --- [nio-8080-exec-5] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#25ff6f87
2019-11-20 13:28:47.565 DEBUG 15456 --- [nio-8080-exec-5] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
Based on the issue with the csrf, I have added the following code to the configuration:
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
// Token is being added to the XSRF-TOKEN cookie.
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
And have tried with removal of antMatchers and just require authenticationi:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class); // Register csrf filter.
}
Any idea, why such behaviour?
I get a token
$ curl -u badge:123456 http://localhost:8080/oauth/token -d grant_type=password -d username=admin -d password=admin -d client_id=badge -d client_secret=123456 -d scope=write
{"access_token":"00a872f9-6f6e-4073-af17-d07d3991c2f0","token_type":"bearer","refresh_token":"8772d67c-682a-4b56-ae51-5a4bc4dceff7","expires_in":43199,"scope":"write"}
Then immediately try to use it
$ curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer 00a872f9-6f6e-4073-af17-d07d3991c2f0" -d '{"apiKey": "key", "tag": "tag"}' localhost:8080/isTagAvailable
But it says I'm anonymous!
2018-05-26 23:43:28.390 DEBUG 54284 --- [nio-8080-exec-5] o.s.security.web.FilterChainProxy : /isTagAvailable at position 7 of 10 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2018-05-26 23:43:28.391 DEBUG 54284 --- [nio-8080-exec-5] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
Why?
OAuth2Configurtion.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(applicationName)
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.authorities("ROLE_USER")
// .scopes("read", "write")
.scopes("write")
.resourceIds(applicationName)
.secret("123456");
}
SecurityConfig.java
#Configuration
#EnableWebSecurity//(debug=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/isTagAvailable").authenticated()
.and()
.authorizeRequests()
.antMatchers("/robots.txt", "/error", "/login", "/doLogout", "/home", "/pageNotFound"
).permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic().disable();
}
Version
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
I added
.anonymous().disable()
But now it gives
2018-05-27 00:42:54.987 DEBUG 54284 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/isTagAvailable'; against '/isTagAvailable'
2018-05-27 00:42:54.987 DEBUG 54284 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /isTagAvailable; Attributes: [authenticated]
2018-05-27 00:42:54.988 DEBUG 54284 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
It still can't remember the token!
I threw away #EnableWebSecurity and WebSecurityConfigurerAdapter which just totally breaks the app. I thought they were required to get access to HttpSecurity which I thought I needed. I discovered this simple new class will solve the problem.
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
String [] ignoredPaths = new String[]{...};
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers(ignoredPaths).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
I am trying to authorize my rest service method for user or admin. But it seems it's not authorizing it and by passing the secured and preAuthorize tags.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true, mode = org.springframework.context.annotation.AdviceMode.ASPECTJ, proxyTargetClass = true)
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
//Some Code Here
}
My application config file:
#EnableWebMvc
#Configuration
#ComponentScan({ "com.ws.service.*" })
#Import({ UserDetailsSecurityConfig.class })
#javax.ws.rs.ApplicationPath("webresources")
public class AppConfig extends Application {
//Some Code Here
}
My Rest service method:
#GET
#Secured("ROLE_ADMIN")
#PreAuthorize("hasRole('ROLE_ADMIN')")
#Path("getUser")
#Produces({"application/xml", "application/json"})
public User GetUser() {
User result;
try{
result = new User();
}catch(NoResultException ne){
result = null;
}
return result;
}
Now either I sign in as admin user with ROLE_ADMIN or simple user with ROLE_USER. Spring Security authorize this method.
I have changed my security logs to fine where it seems that preAuthorize annotation worked. But, in the end gets same result.
Finest: Looking for Pre/Post annotations for method 'GetUser' on target class 'class com.ws.service.UserFacadeREST'
Fine: #org.springframework.security.access.prepost.PreAuthorize(value=hasRole('ROLE_ADMIN')) found on specific method: public com.ws.UpUser com.ws.service.UserFacadeREST.GetUser()
Fine: Caching method [CacheKey[com.ws.service.UserFacadeREST; public com.ws.User com.ws.service.UserFacadeREST.GetUser()]] with attributes [[authorize: 'hasRole('ROLE_ADMIN')', filter: 'null', filterTarget: 'null']]
When I try to log-in it logs me in successfully. I logged in from "user" who has "ROLE_USER" in database.
Fine: /login at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
Fine: /login at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
Fine: HttpSession returned null object for SPRING_SECURITY_CONTEXT
Fine: No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#5094f334. A new one will be created.
Fine: /login at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
Fine: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#50da4786
Fine: /login at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
Fine: /login at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
Fine: Checking match of request : '/login'; against '/logout'
Fine: /login at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
Fine: Checking match of request : '/login'; against '/login'
Fine: Request is to process authentication
Fine: Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
Fine: Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy#72e0fc63
Fine: Delegating to org.springframework.security.web.csrf.CsrfAuthenticationStrategy#5c8272c1
Fine: Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4428690f: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: a50e5eecf72b5493c618ffbb20f0; Granted Authorities: ROLE_USER
Fine: Redirecting to DefaultSavedRequest Url: http://localhost:8080/LmsWebService/webresources/upuser/getUser
Fine: Redirecting to 'http://localhost:8080/LmsWebService/webresources/upuser/getUser'
Fine: SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl#4428690f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4428690f: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: a50e5eecf72b5493c618ffbb20f0; Granted Authorities: ROLE_USER'
Fine: SecurityContextHolder now cleared, as request processing completed
Fine: /webresources/upuser/getUser at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
Fine: /webresources/upuser/getUser at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
Fine: Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl#4428690f: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4428690f: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: a50e5eecf72b5493c618ffbb20f0; Granted Authorities: ROLE_USER'
Fine: /webresources/upuser/getUser at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
Fine: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#50da4786
Fine: /webresources/upuser/getUser at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
Fine: /webresources/upuser/getUser at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
Fine: Checking match of request : '/webresources/upuser/getuser'; against '/logout'
Fine: /webresources/upuser/getUser at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
Fine: Request 'GET /webresources/upuser/getuser' doesn't match 'POST /login
Fine: /webresources/upuser/getUser at position 7 of 14 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
Fine: /webresources/upuser/getUser at position 8 of 14 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
Fine: /webresources/upuser/getUser at position 9 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
Fine: pathInfo: arg1=/upuser/getUser; arg2=/upuser/getUser (property equals)
Fine: queryString: both null (property equals)
Fine: requestURI: arg1=/LmsWebService/webresources/upuser/getUser; arg2=/LmsWebService/webresources/upuser/getUser (property equals)
Fine: serverPort: arg1=8080; arg2=8080 (property equals)
Fine: requestURL: arg1=http://localhost:8080/LmsWebService/webresources/upuser/getUser; arg2=http://localhost:8080/LmsWebService/webresources/upuser/getUser (property equals)
Fine: scheme: arg1=http; arg2=http (property equals)
Fine: serverName: arg1=localhost; arg2=localhost (property equals)
Fine: contextPath: arg1=/LmsWebService; arg2=/LmsWebService (property equals)
Fine: servletPath: arg1=/webresources; arg2=/webresources (property equals)
Fine: Removing DefaultSavedRequest from session if present
Fine: /webresources/upuser/getUser at position 10 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
Fine: /webresources/upuser/getUser at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
Fine: SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4428690f: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: a50e5eecf72b5493c618ffbb20f0; Granted Authorities: ROLE_USER'
Fine: /webresources/upuser/getUser at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
Fine: /webresources/upuser/getUser at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
Fine: /webresources/upuser/getUser at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
Fine: Secure object: FilterInvocation: URL: /webresources/upuser/getUser; Attributes: [authenticated]
Fine: Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#4428690f: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 127.0.0.1; SessionId: a50e5eecf72b5493c618ffbb20f0; Granted Authorities: ROLE_USER
Fine: Voter: org.springframework.security.web.access.expression.WebExpressionVoter#3fb68f13, returned: 1
Fine: Authorization successful
Fine: RunAsManager did not change Authentication object
Fine: /webresources/upuser/getUser reached end of additional filter chain; proceeding with original chain
Info: MonitorFilter::WARNING: the monitor filter must be the first filter in the chain.
Fine: Chain processed normally
Fine: SecurityContextHolder now cleared, as request processing completed
Can anyone tell me what I am doing wrong in all this thing. Or Spring Security doesn't work with glassfish.
My Environment:
Java EE 1.6
Spring Security
GlassFish Server 4.1
MySql Databse
Maven
JPA
EDIT # 1
#Configuration
#EnableWebSecurity
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, mode = org.springframework.context.annotation.AdviceMode.ASPECTJ, proxyTargetClass = true)
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService( new CustomJDBCDaoImpl() );
}
}
I've followed quite a few threads to implement Spring Security to my rest API. Initially I get stuck at #Secured annotation being ignored, now that I got that resolved, I am stuck at getting access denied.
Feels like my problem sound very similar to: #secured with granted authorities throws access denied exception - but I am still getting access denied.
Here's my setup:
spring-security.xml
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"/>
<user-service id="userDetailsService">
<user name="john" password="john1" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="jane" password="jane1" authorities="ROLE_USER" />
<user name="apiuser" password="apiuser" authorities="PERMISSION_TEST" />
</user-service>
Controller:
#Controller
#RequestMapping("/secure")
public class SecureController
{
private static final Logger logger = Logger.getLogger(SecureController.class);
#Secured("PERMISSION_TEST")
#RequestMapping(value = "/makeRequest", method = RequestMethod.GET)
#ResponseBody
public SimpleDTO executeSecureCall()
{
logger.debug("[executeSecureCall] Received request to a secure method");
SimpleDTO dto = new SimpleDTO();
dto.setStringVariable("You are authorized!");
return dto;
}
}
Now - without the proper
<security:global-method-security secured-annotations="enabled"/>
My request goes through (this is because the #Secured annotation is ignored). When I put it in and accessing it using "apiuser"/"apiuser", I kept getting access denied, the debug log:
11:42:43,899 [http-apr-8080-exec-4] DEBUG MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken#cc12af5d: Principal: org.springframework.security.core.userdetails.User#d059c8e5: Username: apiuser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: PERMISSION_TEST; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: PERMISSION_TEST
11:42:43,899 [http-apr-8080-exec-4] DEBUG AffirmativeBased - Voter: org.springframework.security.access.vote.RoleVoter#2a9a42ef, returned: 0
11:42:43,900 [http-apr-8080-exec-4] DEBUG AffirmativeBased - Voter: org.springframework.security.access.vote.AuthenticatedVoter#75a06ec2, returned: 0
11:42:43,902 [http-apr-8080-exec-4] DEBUG AnnotationMethodHandlerExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController#342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,905 [http-apr-8080-exec-4] DEBUG ResponseStatusExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController#342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,906 [http-apr-8080-exec-4] DEBUG DefaultHandlerExceptionResolver - Resolving exception from handler [com.test.webapp.spring.controller.SecureController#342d150f]: org.springframework.security.access.AccessDeniedException: Access is denied
11:42:43,909 [http-apr-8080-exec-4] DEBUG DispatcherServlet - Could not complete request
org.springframework.security.access.AccessDeniedException: Access is denied
Thoughts?
Thanks in advance!
As I remember #Secured annotation works only with role names starting ROLE_ by default.
You may switch to #PreAuthorize("hasAuthority('PERMISSION_TEST')") (with pre-post-annotations="enabled") or rename your role.
I want to add a little more to Michail Nikolaev answer.
My answer is from the source code point of view. I want you to understand why access was denied.
From documentation:
When you use a namespace configuration, a default instance of AccessDecisionManager is automatically registered for you and will be used for making access decisions for method invocations and web URL access, based on the access attributes you specify in your intercept-url and protect-pointcut declarations (and in annotations if you are using annotation secured methods). The default strategy is to use an AffirmativeBased AccessDecisionManager with a RoleVoter and an AuthenticatedVoter.
RoleVoter uses ROLE_ prefix (by default) in order to decide if it can vote. You can change that default prefix with RoleVoter.setRolePrefix() method.
From source code:
public class RoleVoter implements AccessDecisionVoter<Object> {
(...)
private String rolePrefix = "ROLE_";
(...)
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}
(...)
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) &&
attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
} else {
return false;
}
}
(...)
public int vote(Authentication authentication, Object object,
Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
Collection<? extends GrantedAuthority> authorities =
extractAuthorities(authentication);
for (ConfigAttribute attribute : attributes) {
if (this.supports(attribute)) {
result = ACCESS_DENIED;
// Attempt to find a matching granted authority
for (GrantedAuthority authority : authorities) {
if (attribute.getAttribute().equals(authority.getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}
return result;
}
PERMISSION_TEST doesn't start with ROLE_ so RoleVoter abstains from deciding. AuthenticatedVoter abstains too (as you have not used IS_AUTHENTICATED_ prefix in #Secured annotation).
Finally, AffirmativeBased implementation of AccessDecisionManager throws AccessDeniedException because both AccessDecisionVoters abstained from voting.
Java docs for AffirmativeBased:
Simple concrete implementation of
org.springframework.security.access.AccessDecisionManager that grants
access if any AccessDecisionVoter returns an affirmative response.