I have a Spring Boot application, with security. And I have removed the authentication for this "/login" url.
My Security Configuration
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtFilter jwtFilter;
#Autowired
public SecurityConfiguration(JwtFilter jwtFilter) {
this.jwtFilter = jwtFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.anonymous().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.apply(new JwtConfigurerAdapter(jwtFilter)).and()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs");
web.ignoring().antMatchers("/login");
}
}
My NotFound exception
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFound extends RuntimeException {
public NotFound(String message) {
super(message);
}
}
My rest controller with login url and exception return value
#RestController
public class LoginController implements LoginService {
#Override
#GetMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE)
public UserInfo loginUsingJson(String username, String password) {
return findUser(username, password)).orElseThrow(() -> new NotFound("There does not exist any user by those credentials"));
}
}
Okay, here is my problem. When I call GET on "/login" and the UserInfo exists, it will return the user as JSON. This works because of web.ignoring().antMatchers("/login");, but if the user does not exist, then the exception NotFound with the http error code 404, will not show. It is now returning error code 401 Not Authorized.
I'm guessing it has something todo with HttpSecurity, where I have to add some exception or something so the exception code can be returned.
But where do I allow exception handling to be ignored in the authorization of HttpSecurity?
I found the answer, and would like to help others in the same situation.
My problem was that, when returning an rest exception with the errorcode 404 NotFound, Spring Boot would automatically redirect to url "/error". But this url map needs to be opened for business.
So I had to ignore the authorization for this url also.
Here the solution is to add this:
web.ignoring().antMatchers("/error");
And here is the changed class:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtFilter jwtFilter;
#Autowired
public SecurityConfiguration(JwtFilter jwtFilter) {
this.jwtFilter = jwtFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.anonymous().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated().and()
.apply(new JwtConfigurerAdapter(jwtFilter)).and()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs");
web.ignoring().antMatchers("/login");
web.ignoring().antMatchers("/error");
}
}
Related
I am having issue using #PreAuthorize("hasRole('ADMIN')") annotation. My controller code is as below which has method welcome() which can only be accessed by user having role ADMIN:
#CrossOrigin(origins = "*")
#RestController
#RequestMapping("/user/auth")
public class TestController {
#GetMapping("/welcome")
#PreAuthorize("hasRole('ADMIN')")
public String welcome() {
return "Welcome!!!";
}
}
Below is my security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/user/auth/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
AuthEntryPointJwt class is as below:
#Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
}
}
This is the url I am using: http://localhost:8080/user/auth/welcome and this is response
{
"message": null,
"httpStatusCode": 404,
"errorLevelCode": "0x2",
"errorMessage": "Access is denied",
"apiPath": null,
"httpMethod": null
}
So, I am sending jwt Bearer + token in Authorization header using postman and it is throwing 404. It should have returned resource with token after sending authorization header. I am not able to figure out what the problem is. It would be great to have some suggestion or to know something that I am doing wrong over here. Thanks in advance.
Error 404 suggest that you have problem with endpoint mapping (no resource found in path). If it was security issue you would get error 401 (unauthorized) or error 403 (forbidden).
If I am right then you should get same error when you remove #PreAuthorize and add "welcome" path to security config with access "permitAll".
I have seen a lot of posts on the same problem, but no solution worked for me.
I have an API secured with spring security as below.
#EnableWebSecurity
#Component
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired private UserService userService;
public SecurityConfiguration() {
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/actuator/shutdown", "/api/register");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I need endpoints /api/register and /actuator/shutdown available without authentication. But, as it turned out, all the endpoints are returning the same 401 status code.
try with this.
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
//no authentication needed for these context paths
.authorizeRequests()
.antMatchers("/your Urls that dosen't need security/**").permitAll()
We implemented a similar approach as mentioned by Supun Above,
http
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
.and().httpBasic()
You can keep 'AUTH_WHITELIST' as below to keep adding multiple Endpoints
private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/api/register",
"/actuator/shutdown"
};
UPDATE
(Answer by Chids is for the problem that I posted earlier which was getting 403 error for /oauth/token. That error is resolved and am stuck at the next step .I have modified the question accordingly.)
Problem:
I am trying to implement OAuth 2.0 with Spring security. And I am successful in obtaining the access_token by making a post request to /oauth/token.
But when I use this access token to use any other secured url I am getting 403.
I have followed multiple questions on SO but all of them suggest to disable csrf for my problem. Issue is I have already disabled that but still getting error.
Can someone guide me whether I am constructing the post request in a wrong way or whether some config is missing.
My post request through postman looks like:
Config on google:
Resource Server
#Configuration
#EnableResourceServer
#Order(3)
public class Oauth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().antMatchers("/auth/**")
.and()
.authorizeRequests()
.antMatchers("/auth/**").authenticated();
}
}
Authorization server
#Configuration
#EnableAuthorizationServer
public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler handler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authManager;
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("568176070083-1lc20949a0q58l0rhmq93n95kvu8s5o6.apps.googleusercontent.com")
.secret("lNfK3wOaVibgu96il6WLrkTh")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(120)
.refreshTokenValiditySeconds(600);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(handler)
.authenticationManager(authManager);
}
}
Security Config
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#ComponentScan(basePackages = "com.saml.demo")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin123").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.anyRequest().authenticated();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
It should be because, you are disabling all anonymous access in your configure block. You can change it to the following
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/login").permitAll().antMatchers("/oauth/token/revokeById/**").permitAll()
.antMatchers("/tokens/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll().and()
.csrf().disable();
// #formatter:on
}
I have a problem with Spring Security configuration.
When I log in on one computer as a user1 and then I will log in as a user2 on another computer, the first computer after refresh sees everything as a user2.
In other words, it is impossible to have two sessions with different users at the same time.
Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("user1").roles("USER");
auth.inMemoryAuthentication().withUser("user2").password("user2").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter,CsrfFilter.class);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/", "/login").permitAll()
.antMatchers("/questions/**").access("hasRole('USER')")
.and().formLogin().loginPage("/login").defaultSuccessUrl("/questions")
.usernameParameter("ssoId").passwordParameter("password");
}
Spring Security version: 4.0.1.RELEASE
Spring version: 4.1.6.RELEASE
Login request in controller:
#RequestMapping(value = { "/", "/login" }, method = RequestMethod.GET)
public String homePage() {
return "login";
}
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("user1").roles("USER");
auth.inMemoryAuthentication().withUser("user2").password("user2").roles("USER");
}
With that, you are saying that is the user 2 on the session
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(getUser()).password(getPassword()).roles("USER");
}
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("sergey")
.password("{noop}12345678")
.roles("USER")
.and()
.withUser("John")
.password("{noop}87654321")
.roles("MANAGER");
}
}
I just started using Spring boot and Java Based Spring Configurations and I have a problem when trying to test security-related methods such as login and logout.
I have the following configurations in my project
SecurityConfig.java:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.and()
.authorizeRequests()
.antMatchers("/login", "j_spring_security_check", "/register", "/logout").permitAll()
.antMatchers("/**").hasRole("USER");
}
#Autowired
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("guest").password("guest").roles("USER");
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new RestAuthenticationSuccessHandler();
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
SecurityWebAppInitializer.java
#Order(1)
public class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer { }
RestAuthenticationSuccessHandler.java
public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
clearAuthenticationAttributes(request);
return;
}
String targetUrlParam = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() ||
(targetUrlParam != null &&
StringUtils.hasText(request.getParameter(targetUrlParam)))) {
requestCache.removeRequest(request, response);
clearAuthenticationAttributes(request);
return;
}
clearAuthenticationAttributes(request);
}
public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}
}
Now, I want to test the login process so my test class is the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SecurityWebAppInitializer.class, SecurityConfig.class })
public class SecurityTests {
private MockMvc mockMvc;
#Autowired
private FilterChainProxy filterChainProxy;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(SecurityWebAppInitializer.class, SecurityConfig.class).addFilters(filterChainProxy).build();
}
#Test
public void thatLoginWithCorrectCredentialsWorks() throws Exception {
mockMvc.perform(post("/j_spring_security_check")
.param("j_username", "guest")
.param("j_password", "guest")
).andExpect(status().isOk());
}
}
The above test returns the following error
java.lang.AssertionError: Status
Expected :200
Actual :403
which says that the user is forbidden to access the login page. However I don't get why as I've configured in the SpringConfig that a user with the above credentials is allowed to login to my application. I would like to apologise in advance in case my question is silly but I am still trying to improve my knowledge on that framework as I am using it only for a couple of weeks.
EDIT: When I am using CURL to login I am getting the following error:
{"timestamp":1401633376720,"error":"Forbidden","status":403,"message":"Expected CSRF token not found. Has your session expired?"}