Spring security http basic auth for Rest api with Java config - spring

I am trying to secure a CXF based rest API with Spring security. While my configuration technically works, I cannot seem to get the API to respond with JSON rather than an HTML message upon 401. Based on a few other SO posts I put together the following java config, using groovy, for the spring security configuration:
#Configuration
#EnableWebSecurity
#Slf4j
class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) {
http.antMatcher('/api/**')
.authorizeRequests()
.antMatchers('/api/admin/**').hasRole('ADMIN')
.antMatchers('/api/**').hasRole('USER')
.and()
.httpBasic()
.and()
.addFilterBefore(
new BasicAuthenticationFilter(authenticationManager: authenticationManager(), authenticationEntryPoint: new BasicJsonEntryPoint(realmName: 'Local')),
BasicAuthenticationFilter.class
)
}
static class BasicJsonEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest req, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
log.debug 'Handling response'
response.addHeader HttpHeaders.WWW_AUTHENTICATE, /Basic realm="${getRealmName()}"/
response.setStatus HttpStatus.UNAUTHORIZED.value()
response.getWriter().println([status: HttpStatus.UNAUTHORIZED.value(), message: e.getMessage()].toJson())
}
}
}
I've tried numerous variations on this general approach, but no matter what I get HTML back from the API. See the following req/resp:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 401 Full authentication is required to access this resource</title>
</head>
<body>
<h2>HTTP ERROR 401</h2>
<p>Problem accessing /api/test. Reason:
<pre> Full authentication is required to access this resource</pre>
</p>
<hr />
<i>
<small>Powered by Jetty://</small>
</i>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
</body>
</html>

I'm just guessing here, but Jetty is a little too helpful sometimes when your application emits http response codes other than 200s. My recommendation is that you add some logic to your web.xml to short circuit Jetty's helpfulness. The full technique that got my application out of a similar issue is documented on: How do I suppress Jetty 8's default ErrorHandler?
Good luck.

Related

Spring boot and Okta SAML2

I like the idea of using spring-security-saml2-service-provider - from of docs: https://docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/saml2/index.html
Instead of spring-security-saml2-core it looks way less boilerplate, but I catch 400 response when I send App Embed Link from Okta admin app. Through debug it seems that
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
AbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestResolver.resolve(request);
if (authenticationRequest == null) {
filterChain.doFilter(request, response);..}
can't resolve the incoming request,but I am not sure whether it's related.
My yml config:
security:
saml2:
relyingparty:
registration:
okta:
identityprovider:
entity-id: http://www.okta.com/exk1juy5xrR5BsW44697
verification.credentials:
- certificate-location: "classpath:saml/okta.cert"
singlesignon.url: https://trial-8410773.okta.com/app/trial-8410773_templatemanager_2/exk1juy5xrR5BsW44697/sso/saml
singlesignon.sign-request: false
assertingparty.metadata-uri: https://trial-8410773.okta.com/app/trial-8410773_templatemanager_2/exk1juy5xrR5BsW44697/sso/saml/metadata
My Okta config:
GENERAL
Single Sign On URLhttp://localhost:8080/api/v1/saml2/SSO
Requestable SSO URLsURLIndex
http://localhost:8080/api/v1/saml2/SSO0Recipient URLhttp://localhost:8080/api/v1/saml2/SSODestination URLhttp://localhost:8080/api/v1/saml2/SSOAudience Restrictionhttp://localhost:8080/saml/metadata
Also I provide endpoint for saml authentication:
#RequestMapping(SsoAuthenticationController.BASE_NAME)
public interface SsoAuthenticationController {
final String BASE_NAME = "/v1/saml2/SSO";
#GetMapping("/")
public ResponseEntity<HttpStatus> index( Saml2AuthenticatedPrincipal principal) ;
}
Actual security config:
http.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(SECURITY_WHITELIST)
.permitAll()
.anyRequest()
.authenticated()
/*.and()
.httpBasic()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)*/
.and()
.saml2Login(Customizer.withDefaults());
Here is Saml interceptor's logs for google chrome:
https://pastebin.com/Be3NZe5B
Any ideas?
I created a Spring Boot 3 + SAML example with Okta recently. Hopefully, these instructions help.
Create a Spring Boot app using start.spring.io. Select the following options:
Project: Gradle
Spring Boot: 3.0.0 (SNAPSHOT)
Dependencies: Spring Web, Spring Security, Thymeleaf
Add src/main/java/com/example/demo/HomeController.java to populate the authenticated user's information.
package com.example.demo;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomeController {
#RequestMapping("/")
public String home(#AuthenticationPrincipal Saml2AuthenticatedPrincipal principal, Model model) {
model.addAttribute("name", principal.getName());
model.addAttribute("emailAddress", principal.getFirstAttribute("email"));
model.addAttribute("userAttributes", principal.getAttributes());
return "home";
}
}
Create a src/main/resources/templates/home.html file to render the user's information.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
<title>Spring Boot and SAML</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Welcome</h1>
<p>You are successfully logged in as <span sec:authentication="name"></span></p>
<p>Your email address is <span th:text="${emailAddress}"></span>.</p>
<p>Your authorities are <span sec:authentication="authorities"></span>.</p>
<h2>All Your Attributes</h2>
<dl th:each="userAttribute : ${userAttributes}">
<dt th:text="${userAttribute.key}"></dt>
<dd th:text="${userAttribute.value}"></dd>
</dl>
<form th:action="#{/logout}" method="post">
<button id="logout" type="submit">Logout</button>
</form>
</body>
</html>
Create a src/main/resources/application.yml file to contain your metadata URI.
spring:
security:
saml2:
relyingparty:
registration:
assertingparty:
metadata-uri: <your-metadata-uri>
Then, change build.gradle to use thymeleaf-extras-springsecurity6 instead of thymeleaf-extras-springsecurity5 and add Spring Security SAML's dependency:
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.security:spring-security-saml2-service-provider'
To get the metadata URI from Okta, log in to your account and go to Applications > Create App Integration. Select SAML 2.0 and click Next. Name your app something like Spring Boot SAML and click Next.
Use the following settings:
Single sign on URL: http://localhost:8080/login/saml2/sso/okta
Use this for Recipient URL and Destination URL: ✅ (the default)
Audience URI: http://localhost:8080/saml2/service-provider-metadata/okta
Then click Next. Select the following options:
I'm an Okta customer adding an internal app
This is an internal app that we have created
Select Finish.
Okta will create your app, and you will be redirected to its Sign On tab. Scroll down to the SAML Signing Certificates and go to SHA-2 > Actions > View IdP Metadata. You can right-click and copy this menu item's link or open its URL. Copy the resulting link to your clipboard. It should look something like the following:
https://dev-13337.okta.com/app/<random-characters>/sso/saml/metadata
Go to your app's Assignment tab and assign access to the Everyone group.
Paste your metadata URI in to your application.yml file. Start the app using ./gradlew bootRun and open http://localhost:8080 in your favorite browser. You should be redirected to login.

Custom login with Spring Security and OAuth2

I am trying to customize login behavior in my Spring Boot app.
The security configuration of my app is as follows:
#EnableWebSecurity
#Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomAuth2UserService customAuth2UserService;
public SecurityConfiguration(CustomAuth2UserService customAuth2UserService) {
this.customAuth2UserService = customAuth2UserService;
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bundle.js")
.antMatchers("/slds-icons/**")
.antMatchers("/assets/**")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/swagger-resources")
.antMatchers("/v2/api-docs")
.antMatchers("/api/redirectToHome")
.antMatchers("/test/**");
}
public void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/api/", null);
#Override
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
};
http.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher)
.and()
.authorizeRequests()
.antMatchers("/oauth2login").permitAll()
.antMatchers("/manage/**").permitAll()
.antMatchers("/entry-point").permitAll()
.antMatchers("/oauth2/*").permitAll()
.antMatchers("/api/auth-info").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.anyRequest().authenticated()
.and()
.oauth2Login().loginPage("/oauth2login")
.defaultSuccessUri("/")
.userInfoEndpoint()
.userService(customOAuth2UserService);
http.cors().disable();
}
}
The custom OAuth2 user service is as follows:
#Component
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private UserRepository userRepository;
private RoleUserEmailMapRepository roleUserEmailMapRepository;
...
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
DefaultOAuth2User oAuth2User = (DefaultOAuth2User) super.loadUser(userRequest);
Collection<GrantedAuthority> authorities = new HashSet<>(oAuth2User.getAuthorities());
Map<String, Object> attributes = oAuth2User.getAttributes();
...
}
}
The OAuth controller is as follows:
#Controller
public class OAuthController {
private final Logger log = LoggerFactory.getLogger(OAuthController.class);
#GetMapping("/oauth2login")
public String signIn(Model model) {
log.info("Sign in!!");
model.addAttribute("email",
"");
return "first-page";
}
}
first-page.html is as follows:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Enter email</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="container my-5">
<div class="row">
<div class="col-md-6">
<form th:action="#{/redirect-to-auth-provider}" method="post">
<div class="row">
<div class="form-group col-md-6">
<label for="email" class="col-form-label">Email id</label>
<input type="text" th:value="${email}" class="form-control" id="email" placeholder="Email">
</div>
</div>
<div class="row">
<div class="col-md-6 mt-5">
<input type="submit" class="btn btn-primary" value="Submit">
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
OAuth client config properties are as follows:
spring.security.oauth2.client.registration.abc-auth.client-id=123456
spring.security.oauth2.client.registration.abc-auth.client-name=Auth Server
spring.security.oauth2.client.registration.abc-auth.scope=api
spring.security.oauth2.client.registration.abc-auth.provider=abc-auth
spring.security.oauth2.client.registration.abc-auth.client-authentication-method=basic
spring.security.oauth2.client.registration.abc-auth.authorization-grant-type=authorization_code
myapp.oauth2.path=https://abc-auth.com/services/oauth2/
spring.security.oauth2.client.provider.abc-auth.token-uri=${myapp.oauth2.path}token
spring.security.oauth2.client.provider.abc-auth.authorization-uri=${myapp.oauth2.path}authorize
spring.security.oauth2.client.provider.abc-auth.user-info-uri=${myapp.oauth2.path}userinfo
spring.security.oauth2.client.provider.abc-auth.user-name-attribute=name
The Auth provider controller is as follows:
#Controller
public class AuthProviderController {
private final Logger log = LoggerFactory.getLogger(AuthProviderController.class);
#Autowired
private OAuth2ClientProperties properties;
Map<String, String> oauth2AuthenticationUrls
= new HashMap<>();
#RequestMapping(value = "/redirect-to-auth-provider", method = RequestMethod.POST)
public ModelANdView entryPoint(String email) {
log.info("EMAIL RECEIVED AS PART OF REQUEST PARAM = " + email);
List<ClientRegistration> clientRegistrations = new ArrayList<>(
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
String authorizationRequestBaseUri
= "oauth2/authorization/";
String tenantName = email.split("#")[1];
log.info("DERIVED TENANT NAME = " + tenantName);
clientRegistrations.forEach(registration -> //FOR TESTING, HARD-CODED THE VALUES
oauth2AuthenticationUrls.put("abc.com",
authorizationRequestBaseUri + "abc-auth"));
return new ModelAndView("redirect:" + oauth2AuthenticationUrls.get("salesforce.com"));
}
}
Now while I am browsing the end-point of my app: https://localhost:4060(existing end-point of my app), it's successfully redirecting to https://localhost:4060/oauth2login, where I am able to enter email id.
On submit of email id, in the browser network tab, I am seeing: /redirect-to-auth-provider end-point getting invoked, and the following three sequences of calls are happening as expected:
1. https://localhost:4060/oauth2/authorization/abc-auth
2. https://abc-auth.com/services/oauth2/authorize?response_type=code&client_id=123456&scope=api&state=qKPaLcuCp5vtXPlZDQMFKA7Qe4wLApFKtaPB9HOIN0M=&redirect_uri=https://localhost:4060/oauth2
3. https://localhost:4060/oauth2?code=abcdef&state=qKPaLcuCp5vtXPlZDQMFKA7Qe4wLApFKtaPB9HOIN0M=
But, at the end of this call chain, after successful authentication, it's again redirecting to the same page: https://localhost:4060/oauth2login. I want the behavior as, the user gets authenticated properly and after successful authentication, the user should no longer get redirected to https://localhost:4060/oauth2login.
To my surprise, it's working perfectly fine, when I am NOT customizing the login behavior. Then post-authentication, the user is redirected to the home page i.e. https://localhost:4060, and able to properly use the app.
I am not able to figure out what I am missing here.
As I mentioned in an earlier comment, I recommend focusing on a minimal example of your specific problem, instead of a complex application. It helps to learn those basics in small pieces, and it helps us help you more easily if you have a minimal example.
The concept in play here is customizing the redirect_uri. You'll want to thoroughly review the entire section of the docs on OAuth2 Login, specifically the section on the Redirection Endpoint.
It's difficult to tell, but it looks as though you have somehow customized the redirect_uri for the abc-auth client. In order to make this work, you'll need to instruct the OAuth2LoginAuthenticationFilter to use your customized redirect_uri, as in the following example:
#EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login(oauth2 -> oauth2
.redirectionEndpoint(redirection -> redirection
.baseUri("/oauth2/*")
...
)
);
}
}
Note, the actual endpoint for abc-auth will be /oauth2/abc-auth, not /oauth2. It is essential that you have a unique redirect_uri for each oauth client, so you are not vulnerable to attacks such as mix-up (see our SpringOne 2021 presentation for more on this).
As the docs state, you'll also need a property to set your client registration to match:
spring.security.oauth2.client.registration.abc-auth.redirect-uri={baseUrl}/oauth2/{registrationId}

Spring Boot doesn't load javascript file

After Spring starts, I open my ajax.html page, but nothing happens. There is no error message, the js file just doesn't start. If I write javascript-code in ajax.html, it works normally.
ajax.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" xmlns:c="http://xmlns.jcp.org/xml/ns/javaee">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="text/javascript"
src="webjars/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript" src="../static/js/main.js"></script>
</head>
<body>
<div id="fill">
</div>
</body>
</html>
project structure
enter image description here
I had the same issue, and solved it in the following way:
I added the addResourceHandlers to my Java config file:
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**", "/js/**")
.addResourceLocations("classpath:/static/css/", "classpath:/static/js/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
resources folder contains a static/js and a static/css folder both containing the specified files.
Hope it gives you help!
Have you added the resource handler for webjars? See: https://www.baeldung.com/maven-webjars
../static/js/main.js seems wrong. In spring boot, resources in the static directory are referred to without "static" in the URL, so just /js/main.js.
Other than that, are the scripts being loaded? What does the Network tab for the Console say?
As you are using Spring boot, you don't have to add "/static/**" folder in your url. You should miss "/static/" and write like this:
<script type="text/javascript" src="/js/main.js"></script>
And make sure that if you are using spring securiry, you should permit All to access "/js/**" path :
http
.csrf().disable()
.authorizeRequests()
.antMatchers( "/js/**", "/css/**")
.permitAll()
.anyRequest()
.authenticated();

Spring security login fail wrong redirect and no error message

I'm trying to build a simple login form (JS) and to user Spring security. As far as I understood, when login fails, it should redirect to login page (or is that only for JSP login pages inside bootstrap project?) but it fails do to that.
And query Error string parameter is also empty.
My spring security configuration:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable()
.logout()
.permitAll()
.logoutSuccessUrl("/");
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
public UserDetailsService userDetailsService() {
// ensure the passwords are encoded properly
User.UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("user").roles("USER").build());
manager.createUser(users.username("admin").password("admin").roles("USER","ADMIN").build());
return manager;
}
}
Boot:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
From JS app I am sending a request to http://localhost:8080/login, I don't think it matters in this case, but I'm using MithrilJS request:
m.request({
method: "POST",
url: "http://localhost:8080/login",
body: {username: login, password: pass}
})
.then((result) => {
UserLogin.loggedIn = true;
})
.catch(error => {
console.log(error);
});
Responses (2 for some reason) I get:
http://localhost:8080/login?error
Request Method: OPTIONS
Response is empty
error string is also empty
http://localhost:8080/login?error
Request Method: GET
error String is empty
And now the funny part, response contains html (note that I don't have this HTML anywhere in my code):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
</head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/login">
<h2 class="form-signin-heading">Please sign in</h2>
<div class="alert alert-danger" role="alert">Invalid credentials</div> <p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</body></html>
Any ideas where am I failing?
EDIT:
Thank your for the answers, while it did not answer exactly what I had in mind, it did lead me to right direction.
My main problem was that I have 2 separate projects: 1) a spring boot project 2) a JS application. JS application contains form html itself (or JS in this case) since I don't want any front end code to be or come from backend (spring boot project) while all the login logic is in spring boot spring security.
If I disable formLogin (which I have to do, no to use spring login form) I get no /login endpoint.
To summarize, I want to use spring security while bypassing spring login form (this way backend contains login logic, which can be accessed by any form, or that is the idea).
While I'm not quite there yet, I'm getting there.
For anyone that's curious, this is the article that helped: spring security without login form
You are trying to do authentication with ajax, so you can not redirect to any other page dependent on server response, you should do that in you JS(e.g. window.location.href).
Now let's talk about the form login in your case. The UsernamePasswordAuthenticationFilter is enabled based on your configuration.
.formLogin()
.defaultSuccessUrl("/", true)
.permitAll()
This filter will get username and password from the request params.
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
But you are trying to send a json body to the server, so it can not get the right credential. You should change it to a form request.
Next one is about the fail redirect url, now you should know the ajax can not redirect to an other page, the default failureHandler in you configuration will redirect to the login page with error, now you are using ajax, so you just can get the HTML, I think you can just validate the request based on the header(e.g. 401), here is an example.
.formLogin()
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
Here is the code in SimpleUrlAuthenticationFailureHandler
if (defaultFailureUrl == null) {
logger.debug("No failure URL set, sending 401 Unauthorized error");
response.sendError(HttpStatus.UNAUTHORIZED.value(),
HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
You can get the result based on the header and the body.
Now I think your should know the defaultSuccessUrl in your configuration will not work as you expect. You need to implement you own AuthenticationSuccessHandler.
The last one is about your form authentication, the form authentication most of it is based on cookie, I think all your requests should contains the cookie to the server after login successfully. Maybe you can research JWT to instead.
The HTML is the default login form.
Why did you define formLogin()?
You must send username and password in the Authorization header not in the body.
From https://mithril.js.org/request.html
m.request({
method: "POST",
url: "http://localhost:8080/login",
user: login,
password: pass
})
.then((result) => {
UserLogin.loggedIn = true;
})
.catch(error => {
console.log(error);
});

Spring Security 5 authentication always return 302

I'm using Spring-Security 5 to secure my web app. I access /login.jsp and fill in username and password, and then click "Log in" to submit the form, and then was redirected to /login.jsp. I see the reponse status code of that http traffic in fiddler is 302.
SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private DataSource dataSource;
#Autowired
protected SecurityConfig(DataSource dataSource
) {
this.dataSource = dataSource;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.jsp")
.loginProcessingUrl("/login")
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select name userName, password, enabled from user where name=?")
.authoritiesByUsernameQuery("select name userName 'ROLE_USER' from user where name=?")
;
}
}
login.jsp:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post"> 1
<c:if test="${param.error != null}"> 2
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}"> 3
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/> 4
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/> 5
</p>
<button type="submit" class="btn">Log in</button>
</form>
</body>
</html>
This is because spring default authentication success handler looks for a url to redirect.
What one can do is use a custom AuthenticationSuccessHandler
i have used below and no redirects are happening.
public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
protected void handle(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
}
}
Then define the bean and give it in the configure method for security
#Bean
public AuthenticationSuccessHandler appAuthenticationSuccessHandler(){
return new AppAuthenticationSuccessHandler();
}
Configure method
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(appAuthenticationSuccessHandler());
I had this problem until I turned csrf-check off by including .csrf().disable() in configure (HttpSecurity) method.
If you don't have it off then provide csrf token as hidden form field.
... though I see that you have it off disabled
the "loginPage url" same of the "form action"
show me code
java config:
http.formLogin().loginPage("/login.html")
html
<form action="/login.html" method="post">
you just need write controller for "/login.html", by http GET method, Leave the rest to “spring”
docs: https://docs.spring.io/spring-security/site/docs/5.3.6.RELEASE/reference/html5/#servlet-authentication-form
the UsernamePasswordAuthenticationFilter match /login.html by http POST method
My English is not good, Hope I can help you
I don't known if this issue is always active but if this can help someone...
What's works for me was to replace
.formLogin()
by
.httpBasic();
in my WebSecurityConfigurerAdapter class.
So my security config looks like this :
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login", "/actuator/**", "/clients/refresh", "/oauth/token/revokeById/**", "/tokens/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic();
}
Use successHandler to set the referer true. This does the trick for me. Else I am also getting 302.
In securityConfig need to add the below code.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(new RefererRedirectionAuthenticationSuccessHandler ());
}
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
public class RefererRedirectionAuthenticationSuccessHandler extends
SimpleUrlAuthenticationSuccessHandler {
public RefererRedirectionAuthenticationSuccessHandler() {
super();
setUseReferer(true);
}
}
}
Check the below link:
http://www.baeldung.com/spring-security-redirect-login

Resources