I have MasterSecurity config which is to manage login of masters. And
AdminSecurity config which manages admin login.
When I comment one out the other works. But when I try to use both then the master login shows
PostMapping not allowed
package Spring.LoginRegister.Config;
import Spring.LoginRegister.Entity.RolesConstant;
import Spring.LoginRegister.Repository.AdminRepository;
import Spring.LoginRegister.Service.CustomAdminDetailsService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
#AllArgsConstructor
#Order(1)
public class AdminSecurityConfig {
private final AdminRepository adminRepository;
#Bean
public UserDetailsService userDetailsService1(){
return new CustomAdminDetailsService(adminRepository);
}
#Bean
public BCryptPasswordEncoder passwordEncoder1(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider1(){
DaoAuthenticationProvider authProvider= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService1());
authProvider.setPasswordEncoder(passwordEncoder1());
return authProvider;
}
#Bean
public SecurityFilterChain AdminsecurityFilterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider1());
http
.csrf().disable()
.authorizeHttpRequests((request) -> request
.requestMatchers("/AdminDashBoard/**").authenticated()
.requestMatchers("/admin/login").hasRole(RolesConstant.ROLE_ADMIN.toString() )
.anyRequest().permitAll()
)
.formLogin((form) ->form
.loginPage("/admin/login")
.defaultSuccessUrl("/AdminDashBoard", true)
.permitAll()
)
.logout(form -> form
.logoutUrl("/logout")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/")
);
return http.build();
}
}
My problem is they both work individually when one is removed from the app. When I try to combine both only the admin config works properly. This error comes
Method 'POST' is not supported.
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:265)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:441)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:382)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:126)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getHandlerInternal(RequestMappingInfoHandlerMapping.java:68)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:504)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1274)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1056)
This is my second config:
package Spring.LoginRegister.Config;
import Spring.LoginRegister.Repository.MasterRepository;
import Spring.LoginRegister.Service.CustomAdminDetailsService;
import Spring.LoginRegister.Service.CustomMasterDetails;
import Spring.LoginRegister.Service.CustomMasterDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#Order(2)
public class MASTERSecurityConfig {
private MasterRepository masterRepository;
#Bean
public UserDetailsService userDetailsService2(){
return new CustomMasterDetailsService(masterRepository);
}
#Bean
#Primary
public BCryptPasswordEncoder passwordEncoder2(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider2(){
DaoAuthenticationProvider authProvider2= new DaoAuthenticationProvider();
authProvider2.setUserDetailsService(userDetailsService2());
authProvider2.setPasswordEncoder(passwordEncoder2());
return authProvider2;
}
#Bean
public SecurityFilterChain MastersecurityFilterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider2());
http
.csrf().disable()
.authorizeHttpRequests((request) ->request
.requestMatchers("/master/home/**").authenticated()
.requestMatchers("/master/login")
.hasRole(RolesConstant.ROLE_HOUSEMASTER.toString())
.anyRequest().permitAll()
)
.formLogin((form) ->form
.loginPage("/master/login")
.defaultSuccessUrl("/master/home", true)
.permitAll())
.logout(form -> form
.logoutUrl("/logout")
.invalidateHttpSession(true)
.logoutSuccessUrl("/")
.permitAll()
);
return http.build();
}
}
I tried StackOverflow previous answers.
Do not make separate Config classes. Just make multiple security filter chains and order them accordingly. Also, you have to prevent creating Beans of relative components as beans are global and they will contradict with each other.
Related
I am trying to build simple JSON rest API using spring boot, I have build the application it works fine, now I need to do the simple Basic authentication and I did this with Spring security. Please find the Code below, My pain point here is when I run the application, I ask for the login first time then I tried to reload the URL again it does not ask for user name password. How to make the URL session expired and ask to prompt for login again. Note : I am not using any UI, It just an API
Security Configuration Class
package com.erplogic.erp.topcon.security;
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.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
public class SecurityConfiguration {
#Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated()
).sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
)
.httpBasic();
return http.build();
}
#Bean
InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder().username("user").password(encoder().encode("pass")).roles("USER").build();
return new InMemoryUserDetailsManager(user);
}
#Bean
PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
Controller Class
package com.erplogic.erp.topcon.controller;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.erplogic.erp.topcon.service.SapApiService;
import com.erplogic.poc.objects.Root;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.erplogic.maven.plclog.wsdl2java.StandardFaultMessage_Exception;
#RestController
public class OutboundController {
#Autowired
private SapApiService sapApiService;
#RequestMapping(path ="/outbound",produces = MediaType.APPLICATION_JSON_VALUE,method = RequestMethod.GET)
public Root getOutbound() throws KeyManagementException, NoSuchAlgorithmException, StandardFaultMessage_Exception, JsonProcessingException {
return ((SapApiService) sapApiService).processOutboudService();
}
#RequestMapping(value = "/outbound/{outboundId}",produces = MediaType.APPLICATION_JSON_VALUE,method = RequestMethod.GET)
public String getOutboundId(#PathVariable String outboundId) throws KeyManagementException, JsonProcessingException, NoSuchAlgorithmException, StandardFaultMessage_Exception {
return ((SapApiService) sapApiService).processOutboudServiceByID(outboundId);
}
}
All:
I have a basic program for Ldap authentication which returns a "Principal User "
package com.bpm.cbl.premium.controller;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
#RestController
#RequestMapping("custom")
public class LDAPAuthController {
public static String domain;
public static String URL;
#Value("${activedirectory.domain}")
private String adDomain;
#Value("${activedirectory.url}")
private String adURL;
#PostConstruct
public void init(){
domain = adDomain;
URL = adURL;
}
#GetMapping("/user-login")
#ResponseBody
public Principal user(Principal user) {
return user;
}
#Configuration
#Order(SecurityProperties.BASIC_AUTH_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.logout().and()
.authorizeRequests()
.antMatchers("/index.html", "/", "/home", "/login", "/assets/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new
ActiveDirectoryLdapAuthenticationProvider(domain, URL);
return activeDirectoryLdapAuthenticationProvider;
}
}
}
I dont know how to return a cookie or token instead of a object .. Iam new to spring security..Can someone help pls
I have reference to another post but not sure whether it will work how to achieve Ldap Authentication using spring security(spring boot)
Can someone pls provide some inputs pls
Ok I got a solution; Posting for the benefit of all..
There are lot of confusing articles in the internet and many forums but it is very simple
Replace the function under #GetMapping("/user-login") above with a function that returns the cookie in the respose body.. Pass httpserveletresponse as argument for the function along with any other arguments needed.. Thats it the cookie will be returned in the response header;
I have an Spring Boot App (2.1.6) implemented with Kotlin. Is a Rest api that wants to have oAuth 2 with Keycloak.
I have this code in Java that compiles ok:
package com.talleres.paco.mako.config.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ConditionalOnProperty(prefix = "rest.security", value = "enabled", havingValue = "true")
#Import({SecurityProperties.class})
public class SecurityConfigurer extends ResourceServerConfigurerAdapter {
#Autowired
private ResourceServerProperties resourceServerProperties;
#Autowired
private SecurityProperties securityProperties;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceServerProperties.getResourceId());
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.cors()
.configurationSource(corsConfigurationSource())
.and()
.headers()
.frameOptions()
.disable()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(securityProperties.getApiMatcher())
.authenticated();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
if (null != securityProperties.getCorsConfiguration()) {
source.registerCorsConfiguration("/**", securityProperties.getCorsConfiguration());
}
return source;
}
#Bean
public JwtAccessTokenCustomizer jwtAccessTokenCustomizer(ObjectMapper mapper) {
return new JwtAccessTokenCustomizer(mapper);
}
#Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails details) {
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(details);
//Prepare by getting access token once
oAuth2RestTemplate.getAccessToken();
return oAuth2RestTemplate;
}
}
When i convert to Kotlin i obtain a syntax error:
package com.talleres.paco.mako.config.security
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.oauth2.client.OAuth2RestTemplate
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ConditionalOnProperty(prefix = "rest.security", value = ["enabled"], havingValue = "true")
#Import({SecurityProperties.class})
class SecurityConfigurer : ResourceServerConfigurerAdapter() {
#Autowired
private val resourceServerProperties: ResourceServerProperties? = null
#Autowired
private val securityProperties: SecurityProperties? = null
#Throws(Exception::class)
override fun configure(resources: ResourceServerSecurityConfigurer) {
resources.resourceId(resourceServerProperties!!.resourceId)
}
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.cors()
.configurationSource(corsConfigurationSource())
.and()
.headers()
.frameOptions()
.disable()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(securityProperties!!.apiMatcher)
.authenticated()
}
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val source = UrlBasedCorsConfigurationSource()
if (securityProperties?.corsConfiguration != null) {
source.registerCorsConfiguration("/**", securityProperties.corsConfiguration);
}
return source
}
#Bean
fun jwtAccessTokenCustomizer(mapper: ObjectMapper): JwtAccessTokenCustomizer {
return JwtAccessTokenCustomizer(mapper)
}
#Bean
fun oauth2RestTemplate(details: OAuth2ProtectedResourceDetails): OAuth2RestTemplate {
val oAuth2RestTemplate = OAuth2RestTemplate(details)
oAuth2RestTemplate.accessToken
return oAuth2RestTemplate
}
}
The error is in the line with the annotation import:
#Import({SecurityProperties.class})
I convert the code from Java to Kotlin with IntelliJ CE. The message is:
> Task :compileKotlin
e: D:\Workspaces\CleanArchitecture\mako\src\main\customized\kotlin\com\talleres\paco\mako\config\security\SecurityConfigurer.kt: (26, 34): Name expected
Thanks in advance.
I guess the tool that converts Java code to Kotlin is not always working. In this case #Import is defined in Java and it expects an array of classes, and you can use it in Kotlin by passing a vararg KClass (actually that works only for annotation's value field, otherwise you need to pass a proper array. More info here).
In other words, you need to change your code to: #Import(SecurityProperties::class).
EDIT: it seems like this issue was reported and fixed years ago: KT-10545. I also tried converting your Java code to Kotlin on my machine (using Kotlin 1.3.41) and the #Import statement was correctly generated.
Something funny, though, happened. The #ConditionalOnProperty line was converted to this:
#ConditionalOnProperty(prefix = "rest.security", value = "enabled", havingValue = "true")
which doesn't compile as "Assigning single elements to varargs in named form is forbidden". I'd be curious to see if it's a regression, as it looks correct in your snippet.
I wanted to pass in JSON instead of using params while logging in. So what I do is I create a filter, however, the strange thing is that the filter itself doesn't get invoke at all (Or basically when I try logging in, the request by pass it, completely ignore my filter). The request go straight to my AuthenticationHandler. I have gone through many posts and I still have no clue of why would this happen, especially when I replicate the same structure of code in Java but it works perfectly as intended...
Did I miss something obvious? Here's the UsernamePasswordAuthenticationFilter and my security config. My Java version works fine, but my Kotlin version completely ignores the filter.
It doesn't return 404 as well, it returns my AuthenticationFailureHandler.
import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import lombok.Getter
import org.apache.commons.io.IOUtils
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationServiceException
import org.springframework.security.authentication.InternalAuthenticationServiceException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.io.IOException
import java.nio.charset.Charset
class JsonLoginFilter : UsernamePasswordAuthenticationFilter() {
#Throws(AuthenticationException::class)
override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse?): Authentication {
if (!HttpMethod.POST.matches(request.method)) {
throw AuthenticationServiceException("Authentication method not supported: " + request.method)
}
val payload: String
try {
payload = IOUtils.toString(request.inputStream, Charset.defaultCharset())
val auth = ObjectMapper().readValue(payload, JsonAuthenticationParser::class.java)
// println(auth.username)
// println(auth.password)
val authRequest = UsernamePasswordAuthenticationToken(auth.username, auth.password)
return this.authenticationManager.authenticate(authRequest)
} catch (e: IOException) {
throw InternalAuthenticationServiceException("Could not parse authentication payload")
}
}
#Getter
data class JsonAuthenticationParser #JsonCreator
constructor(#param:JsonProperty("username")
val username: String,
#param:JsonProperty("password")
val password: String)
}
My Security config in Kotlin
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler
#EnableWebSecurity
class WebSecurity: WebSecurityConfigurerAdapter() {
#Autowired
private lateinit var entryConfig: EntryConfig
#Autowired
private lateinit var failAuth: FailAuthentication
#Autowired
private lateinit var successAuthentication: SuccessAuthentication
#Autowired
private lateinit var authenticationHandler: AuthenticationHandler
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.antMatchers("/api/v1/traveller/add","/api/v1/symptoms","/api/v1/flights","/api/v1/user/login","/api/v1/user/logout").permitAll()
.antMatchers("/api/v1/user/**","/api/v1/traveller/**").hasRole("ADMIN")
.antMatchers("/**").authenticated()
.and()
.addFilterAt(authenFilter(), UsernamePasswordAuthenticationFilter::class.java)
.formLogin().loginProcessingUrl("/api/v1/user/login")
.successHandler(successAuthentication).failureHandler(failAuth)
.and()
.exceptionHandling().authenticationEntryPoint(entryConfig)
.and()
.cors()
.and()
.logout().logoutUrl("/api/v1/user/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(HttpStatusReturningLogoutSuccessHandler())
.permitAll()
//
http
.csrf()
.disable()
}
#Throws(Exception::class)
override fun configure(auth: AuthenticationManagerBuilder) {
auth.authenticationProvider(authenticationHandler)
}
#Bean
#Throws(Exception::class)
fun authenFilter(): JsonLoginFilter {
var filter : JsonLoginFilter = JsonLoginFilter()
filter.setAuthenticationManager(authenticationManagerBean())
filter.setAuthenticationSuccessHandler(successAuthentication)
filter.setAuthenticationFailureHandler(failAuth)
return filter
}
#Bean
fun passwordEncoder(): BCryptPasswordEncoder {
return BCryptPasswordEncoder()
}
}
My Java version, slightly differ but I believe it should have the same structure
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("hello");
if (! HttpMethod.POST.matches(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String payload;
try {
payload = IOUtils.toString(request.getInputStream(), Charset.defaultCharset());
JsonAuthenticationParser auth = new ObjectMapper().readValue(payload, JsonAuthenticationParser.class);
System.out.println(auth.username);
System.out.println(auth.password);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(auth.username, auth.password);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
throw new InternalAuthenticationServiceException("Could not parse authentication payload");
}
}
#Getter
static class JsonAuthenticationParser {
private final String username;
private final String password;
#JsonCreator
public JsonAuthenticationParser(#JsonProperty("username") String username, #JsonProperty("password") String password) {
this.username = username;
this.password = password;
}
}
}
Security config in Java
import hard.string.security.AuthenticationHandler;
import hard.string.security.EntryConfig;
import hard.string.security.FailAuthhentication;
import hard.string.security.SuccessAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private EntryConfig entryConfig;
#Autowired
private FailAuthhentication failAuth;
#Autowired
private SuccessAuthentication successAuthentication;
#Autowired
private AuthenticationHandler authenticationHandler;
#Bean
public JsonAuthenticationFilter authenticationFilter() throws Exception {
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
// filter.setContinueChainBeforeSuccessfulAuthentication(true);
filter.setAuthenticationSuccessHandler(successAuthentication);
filter.setAuthenticationFailureHandler(failAuth);
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// http://stackoverflow.com/questions/19500332/spring-security-and-json-authentication
http
.authorizeRequests()
.antMatchers("/login", "/logout", "/register",
"/debug/**").permitAll()
.antMatchers("/**").authenticated()
.and()
.addFilterAt(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.formLogin().loginProcessingUrl("/login")
.successHandler(successAuthentication).failureHandler(failAuth)
.and()
.exceptionHandling().authenticationEntryPoint(entryConfig)
.and()
.cors()
.and()
.logout().logoutUrl("/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
.permitAll();
//
http
.csrf()
.disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationHandler);
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Thanks for the help
Ok, After spending days to find the bug. I found out that the filter doesn't automatically link with loginProcessingUrl. You need to specify what url you want to do the filter on or else it will just apply the filter only to localhost:xxxx/login
I just going to leave this question up here just in case someone run into this stupid problem like myself.
fun authenFilter(): JsonLoginFilter {
var filter : JsonLoginFilter = JsonLoginFilter()
filter.setAuthenticationManager(authenticationManagerBean())
filter.setAuthenticationSuccessHandler(successAuthentication)
filter.setAuthenticationFailureHandler(failAuth)
filter.setFilterProcessesUrl("/api/v1/user/login") //HERE
return filter
}
I'am using swagger version 2.2.2. If I go to the address, http://localhost:8080/swagger-ui.html
, I will directly get the swagger UI. Is there any way, a security layer can be added, like, user should be prompted to enter for user id and password before the swagger UI display?
It is possible, I did it with this security configuration (assuming you are using spring):
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
manager.createUser(User.withUsername("user").password(encoder.encode("password")).roles("ACTUATOR", "ADMIN").build());
http.authorizeRequests().antMatchers("/swagger-ui**", "/v2/api-docs/**").hasRole("ACTUATOR").and().httpBasic()
.and().userDetailsService(manager);
}
}