spring security implementation to secure REST APIs in my spring boot project - spring

I have a RestController which has one API that is used to authenticate a user. I want this API to be accessible by anyone whether authenticated or not and irrespective of their roles. In other words, this API should be called when someone types in their username and password and presses submit button.
Here is the config(HttpSecurity http) method of security config java file
#Override
protected void configure(HttpSecurity http) throws Exception {
http/* .csrf().disable() */
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("CUSTOMER")
.antMatchers("/supplier/**").hasRole("SUPPLIER")
.antMatchers("/user/authenticate").permitAll()
.antMatchers("/user/**").authenticated()
.and()
.formLogin().permitAll()
.and()
.logout()
.permitAll();
}
In the above code, I've written ''antMatchers("/user/authenticate").permitAll()'' because I want this url to be accessible by everyone and whatever logic is written in the controller should be executed.
And here is my controller
#RestController
#RequestMapping("/user")
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#PostMapping("/authenticate")
public void authenticate(#RequestBody AuthenticationRequest request) {
Authentication token = authenticationManager.authenticate(new
UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
// more code
}
}
But I don't know why it is not working. When I send a POST request from POSTMAN, I get the following response back:
{
"timestamp": "2020-07-24T08:50:02.514+00:00",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/user/authenticate"
}
Someone please suggest what I should do to make it hit my REST controller

Related

SAML with Spring security anonymousUser

We have to implement SSO using SAML in SpringBoot. I have achieved to redirect the user to the identity provider login page and make the login. The problem is that after the login, when I try to get the user info with SecurityContextHolder.getContext().getAuthentication() I get anonymousUser, and not the logged user data.
Here is my SecurityConfig
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().and()
.authorizeRequests()
.antMatchers("/saml/**).permitAll()
.anyRequest().authenticated()
.and()
.apply(saml())
.userDetailsService(samlUserDetailsServiceImpl)
.serviceProvider()
.protocol("http")
.hostname("localhost:8080")
.basePath("/")
.keyStore()
.storeFilePath("classpath:metadata/samlKeystore.jks")
.keyPassword(keystorePass)
.keyname(keystoreAlias)
.and()
.and()
.identityProvider()
.metadataFilePath("classpath:metadata/idp.xml")
.discoveryEnabled(false)
.and()
.and();
And the implementation of SAMLUserDetailService
#Service
public class SamlUserServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) {
String userID = credential.getNameID().getValue();
GrantedAuthority userAuthority = new SimpleGrantedAuthority("ROLE_GESTOR");
return new User(userID, "DUMMY", Collections.singletonList(userAuthority));
}
I have debugged the code and in the SAMLUserDetailsService implementation I receive the user data after logging.
Also, is there a way to indicate the redirection url when the user has logged? Now it redirects to same url.
Thanks in advance

Springboot configuration 401 Unauthorized

I have this configure method and i want to make user be able to register but i get 401 Unathorized. It is caused by the .apply(**) and i am not able to do it.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/register").permitAll()
.antMatchers("/auth/signin").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider, securityUtils));
}
JwtConfigurer.class
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenProvider jwtTokenProvider;
private final SecurityUtils securityUtils;
public JwtConfigurer(JwtTokenProvider jwtTokenProvider, SecurityUtils securityUtils) {
this.jwtTokenProvider = jwtTokenProvider;
this.securityUtils = securityUtils;
}
#Override
public void configure(HttpSecurity http) {
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider, securityUtils);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
So when request is sent to /auth/register i dont want to add .apply(**). Do u have any suggestion please?
In your class that extends WebSecurityConfigurerAdapter where your http configure() method with .apply() is written, you can use the following to tell Spring Boot to bypass or ignore the filter if encountered with the uri for user registration.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/auth/register");
}
Edit: Since you are facing the exception:
Cross-origin Request Blocked (Reason: CORS header ‘Access-Control-Allow-Origin’ missing and Reason: CORS request did not succeed)
it means that your OPTIONS request (preflight request) is failing. Are you using a different project as Front End for your application? If yes, you will need to specify in your spring boot configuration to allow origin and allow the specified methods from that particular origin.
Please refer to this official documentation to learn how to do that. You can enable Cors at Controller level or at global level. This StackOverflow thread should also be helpful in doing the implementation in case you are unable to proceed.

Spring Boot Security Authentication from third API

I build a web app using spring boot + thymeleaf,
but this project is client (not backend / not using database), i'm consuming third API (login, store data,load data,update data, delete data),
i have a problem when implementation spring boot security, username and password authentication with third API,
this endpoint for login auth (third API)
http://kuala/app/directory/user/login?j_username=admin&j_password=admin
success response
{
"isAdmin": "true",
"username": "admin"}
failed response
{
"error": {
"date": "Fri Jan 24 10:29:26 ICT 2020",
"code": "401",
"message": ""
}}
this sample SecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.antMatchers("/user/**").hasAuthority("USER")
.anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/home")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.userDetailsService(appUserDetailsService);
}}
any one help me,
thanks in advance
best regard
Khafidz
One way is to create your own AuthenticationManager bean, and delegate the authenticate call to the 3rd party (using a RestTemplate, for example):
#Component
public class CustomAuthenticationManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// Call your 3rd party API and return an Authentication object based
// on its response
ResponseEntity loginResponse = restTemplate.exchange(...);
if(loginResponse.getStatusCode() == HttpStatus.OK) {
// create a valid Authentication object with roles, etc
}
else {
// throw an exception such as BadCredentialsException
}
}
}

Spring Security OAuth - how to disable login page?

I want to secure my application with Spring Security, using OAuth 2. However, I don't want the server to redirect incoming unauthorized requests, but instead to respond with HTTP 401. Is it possible?
Example: this code redirects requests to a default login page.
application.properties
spring.security.oauth2.client.registration.google.client-id=...
spring.security.oauth2.client.registration.google.client-secret=...
AuthConfig.java
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
// https://stackoverflow.com/questions/31714585/spring-security-disable-login-page-redirect
// deos not work
// .and()
// .formLogin().successHandler((request, response, authentication) -> {});
}
}
You need to create new authentication entry point and set it in configuration.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
}
}
public class AuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AuthenticationEntryPoint() {
super("");
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(401, "Unauthorized");
}
}
You need to set oauth2Login.loginPage in your HttpSecurity config and create a controller mapping to return whatever you want. Here's a simple example.
So in your security config
http
.authorizeRequests()
.antMatchers("/noauth").permitAll()
.oauth2Login()
.loginPage("/noauth")
In a controller
#GetMapping("/noauth")
public ResponseEntity<?> noAuth() {
Map<String, String> body = new HashMap<>();
body.put("message", "unauthorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(body);
}
You can pass a map or pojo to the body method.
I would like to expand on Petr's answer by explaining that apparently for the time being first of all, the default login page is shown when there are more than one OAuth2 configured providers. I would expect that Spring Boot would have a smart trick to bypass this page easily and choose the right provider automatically, basing e.g. on the existence of the provider's client ID in the original request. I found out the hard way that this is not the case. So the way to do this is.. this not very apparent trick of providing a custom handler for failures - that will REDIRECT the user to the correct OAuth2 endpoint for each provider, based on the original HTTP request URL. I tried this and it works and I spent a whole day trying all manners of other solutions - my original scenario was to pass additional parameters to OAuth2 scheme in order to be able to get them back on successful authentication - they used to do this appending Base64 encoded information to the "state" URL request parameter, but Spring Security does not allow this at the moment. So the only alternative was to call a Spring Security-protected URL with those parameters already there, so when the successful authentication happens, this URL is accessed again automatically with those parameters intact.
Related: Multiple Login endpoints Spring Security OAuth2

"status": 403, "error": "Forbidden", "message": "Forbidden", "path": "/post/create"

I see this response when I try to add new post after authorization by admin.
I have Basic authorization which based on spring boot security:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//...declared fields
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user")
.password("userpass")
.roles("USER")
.and()
.withUser("admin")
.password("adminpass")
.roles("ADMIN", "USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and().logout().permitAll()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I get this message when try to add new post after authorization:
{
"timestamp": "2018-07-04T12:19:25.638+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/post/create"
}
in my controller:
#RestController
public class PostController {
#Autowired
private PostDAO postDAO;
#GetMapping("/posts")
public Page<Post> getAllPosts(Pageable pageable) {
return postDAO.findAll(pageable);
}
#PostMapping("/post/create")
public Post createPost(#Valid #RequestBody Post post) {
return postDAO.save(post);
}
//other end-points........
}
However, read operations from my controller work well but to CRUD operation I haven't access.
There are my dependencies:
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.hibernate:hibernate-core')
compile('org.springframework.boot:spring-boot-starter-security')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
testCompile('junit:junit')
}
Any idea?
Thanks in advance!
This is due to CSRF enabled. CSRF protection is enabled by default in the Java configuration. We can still disable CSRF using the configuration given below.
http .csrf().disable() .authorizeRequests() .anyRequest().permitAll();
Starting from Spring Security 4.x – the CSRF protection is enabled by default in the XML configuration as well; we can of course still disable it if we need to:
<http>
...
<csrf disabled="true"/>
</http>
Note : CSRF is an attack which forces an end user to execute unwanted
actions in a web application in which is currently authenticated.
here's why:
csrf is automatically enabled in spring security,and I recommended you do not disable csrf.
normally your html form tag should include a hidden field which generates csrf token, however, thymeleaf automaticlly do that for you, you should check your html tag to see whether or not a "th:" was included, if not, include a "th:" before "action" in form tag, do this, thymeleaf generates csrf token invisibablly.

Resources