Spring Security Basic authentication for a single path next to token authentication - authentication

I have a custom ResourceServerTokenServices in place:
#Configuration
public class CloudSecurityConfig {
#Bean
protected MyResourceServerTokenServices() {
return new MyResourceServerTokenServices();
}
}
Then I have follogin ResourceServerConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityResourceConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests().accessDecisionManager(accessDecisionManager())
.antMatchers("/h2-console/**").anonymous()
.antMatchers("/health/**").permitAll()
.antMatchers("/v*/api-docs").permitAll().anyRequest()
.authenticated().and().httpBasic().and().headers()
.frameOptions().disable();
}
#Bean
protected UnanimousBased accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> voterList = new ArrayList<>();
WebExpressionVoter expressionVoter = new WebExpressionVoter();
expressionVoter.setExpressionHandler(new OAuth2WebSecurityExpressionHandler());
voterList.add(expressionVoter);
voterList.add(new AuthenticatedVoter());
return new UnanimousBased(voterList);
}
}
Now I need to add basic authentication (with inMemory credentials) for one single endpoint (lets say /myEndpoint**). How can I achieve this?
Thanks!

Related

Unable to authenticate with UsernameAndPasswordToken and set Cookie with Spring Security

I am building Spring Boot/ReactJS app and i have following issue in authenticating the user and generating JSSESSIONID.
My SecurityConfig class looks like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.logout().deleteCookies("JSESSIONID")
.and()
.rememberMe().key("uniqueAndSecret").tokenValiditySeconds(86400)
.and()
.csrf().disable();
}
#Bean
BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder();}
}
And my LoginController:
#RestController
#RequestMapping(path="/",produces = "application/json")
#CrossOrigin(origins="*")
public class LoginController {
private final AuthenticationManager authenticationManager;
public LoginController(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#PostMapping("/login")
#ResponseStatus(HttpStatus.OK)
public void login(HttpServletRequest req, #RequestBody LoginRequest loginRequest) {
UsernamePasswordAuthenticationToken authReq
= new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication auth = authenticationManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
}
}
And when i call request, i have a BadCredentials exception thrown when calling authenticationManager.authenticate(authReq)
because the authReq's content log is
org.springframework.security.authentication.UsernamePasswordAuthenticationToken#6582813: Principal: pavel; Credentials: [PROTECTED]; Authenticated: false; Details: null; Not granted any authorities
Am i missing some part of configuration here?

ClientHttpRequestInterceptor not called in springboot

I am trying to add logging to my application using ClientHttpRequestInterceptor.My interceptor is not being called.
Not sure what is going wrong here -
Here is my code -
#Component
#Slf4j
public final class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
protected static final LoggingAspect aspect = new LoggingAspect();
private final RequestContext requestContext;
private boolean logResponseBody = true;
public RestTemplateInterceptor(RequestContext requestContext) {
this.requestContext = requestContext;
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
populateHeader(request);
traceRequest(request, body);
ClientHttpResponse response = clientHttpRequestExecution.execute(request,body);
traceResponse(response);
return response;
}
private void populateHeader(HttpRequest request) {
final HttpHeaders headers = request.getHeaders();
// Propagate TAM headers
headers.add("iv-user", requestContext.getUser());
headers.add("MessageId", requestContext.getMessageId());
headers.add("CorrelationId", requestContext.getConversationId());
headers.add("BusinessId", requestContext.getBusinessId());
headers.add("ApplicationName", requestContext.getSourceSystem());
headers.add("iv-groups", requestContext.getGroups());
headers.add("MessageDateTime", requestContext.getSourceTimestamp());
}
...................
Here is my config file
#Configuration
public class RestTemplateConfig {
/**
* initialise restTemplate
*
* #param restTemplateInterceptor autowired in RestTemplateInterceptor
* #return
*/
#Bean
public RestTemplate restTemplate(ClientHttpRequestInterceptor restTemplateInterceptor, ObjectMapper objectMapper) {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(restTemplateInterceptor);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
Here is my WebMVC file
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public WebMvcConfigurer webAuthentication() {
return new WebMvcConfigurer() {
#Override
public void addInterceptors(InterceptorRegistry registry) {
//registry.addInterceptor(myInterceptor());
registry.addInterceptor(new MVCLoggingInterceptor()).addPathPatterns("/api/**");
registry.addInterceptor(new WebAuthentication()).addPathPatterns("/api/**/");
}
};
}
}
Here is my application file
#EnableAsync
#EnableScheduling
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class XManagementApplication {
public static void main(String[] args) {
SpringApplication.run(XManagementApplication.class, args);
}
}
Can anybody tell why my interceptor class is not called when I try to call any API
Any help would be appreciate?
I don't really understand why you want to instantiate your RestTemplateInterceptor as a Bean. Why not simply instantiate your interceptor inside the method RestTemplateConfig.restTemplate() ?
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
Btw, why do you need to pass RequestContext to the constructor of your interceptor ?

Spring-security/Grails app - Custom WebSecurity configurations not getting called

My project is based on Grail 2.5.6 and Spring plugins. I'm trying to create a custom auth provider, filter and token extending their respective basic classes.
this.getAuthenticationManager().authenticate(authRequest)
In my filter the authentication manager is always null. So, it throws cannot invoke authenticate() on a null object. When I debug on the authenticationManager, it lists other provider names but my custom one.
Here is my custom web security config
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
OrbisAuthenticationProvider orbisAuthenticationProvider
public CustomWebSecurityConfig() {
super()
log.debug "configure custom security"
print("configure custom security")
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
print("configure method 1")
log.debug "configure method 1"
auth.authenticationProvider(orbisAuthenticationProvider)
}
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
#Override
AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean()
}
#Bean
OrbisAuthenticationFilter orbisAuthenticationProvider() throws Exception {
log.debug "orbis Authentication provider"
OrbisAuthenticationProvider orbisAuthenticationProvider = new OrbisAuthenticationProvider(authenticationManagerBean())
return orbisAuthenticationProvider
}
#Bean
#Autowired
public OrbisAuthenticationFilter orbisAuthenticationFilter() throws Exception {
print("configure orbis filtr")
OrbisAuthenticationFilter oaf = new OrbisAuthenticationFilter()
oaf.setAuthenticationManager(authenticationManagerBean())
oaf.setFilterProcessesUrl("j_orbis_security_check")
oaf.setUsernameParameter("email")
oaf.setPasswordParameter("password")
oaf.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler()
.setDefaultTargetUrl("/oauth/authorize"))
oaf.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()
.setDefaultFailureUrl("/loginWithOrbis"))
oaf.afterPropertiesSet()
return oaf
}
}
On debugging, it doesn't look like any of these methods are getting called. The annotations don't seem enough to get picked up. I had tried #ComponentScan too.
Do I have to inject this security config somewhere? How do I get authManager to be available in my filter?
OrbisAuthFilter
class OrbisAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
// #Autowired
OrbisAuthenticationProvider orbisAuthenticationProvider
OrbisAuthenticationFilter() {
super("/j_orbis_security_check")
orbisAuthenticationProvider = new OrbisAuthenticationProvider()
}
void afterPropertiesSet() {
assert authenticationManager != null, 'authenticationManager must be specified'
}
#Override
Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("email")
String password = request.getParameter("password")
String accessCode = request.getParameter("accessCode")
OrbisAuthenticationToken authRequestForAuthentication = new OrbisAuthenticationToken(username, password, accessCode)
// This throws error because getAuthenticationManager returns null
// authRequestForAuthentication = this.getAuthenticationManager.authenticate(authRequestForAuthentication)
//This works if I instantiate the orbis provider object in the constructor
authRequestForAuthentication = this.orbisAuthenticationProvider.authenticate(authRequestForAuthentication)
SecurityContextHolder.getContext().setAuthentication(authRequestForAuthentication)
return authRequestForAuthentication
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
#Override
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
}
OrbisAuthProvider
class OrbisAuthenticationProvider implements AuthenticationProvider {
#Override
Authentication authenticate(Authentication authentication) throws AuthenticationException {
OrbisAuthenticationToken orbisAuth = (OrbisAuthenticationToken) authentication
String username = orbisAuth.principal
String password = orbisAuth.credentials
String orbisAccessCode = orbisAuth.orbisAccessCode
def urlToUse = 'https://coopstatus.neu.edu/sail_api/full.aspx?' + 'ac=' + orbisAccessCode + '&e='+ username + '&p=' + password
HttpClient httpClient = DefaultHttpClient.newInstance()
HttpGet getRequest = new HttpGet(urlToUse)
HttpResponse httpResponse = httpClient.execute(getRequest)
JSONObject orbisResponse = new JSONObject(httpResponse.getEntity().getContent().getText())
// if(orbisResponse.get("IsFound")) {
// //Return error not authenticated
// }
Collection<GrantedAuthority> orbisUserGrantedAuthorities = getLDAPUserAuthorities(orbisResponse.get("Email"))
orbisAuth = new OrbisAuthenticationToken(username, password, orbisAccessCode, orbisUserGrantedAuthorities)
return orbisAuth
}
private Collection<GrantedAuthority> getLDAPUserAuthorities(String username) {
LDAPUserDetails currentLdapUserDetails
try {
currentLdapUserDetails = new LDAPUserDetailsService().loadUserByOrbisUsername(username)
log.debug currentLdapUserDetails
} catch(org.springframework.security.core.userdetails.UsernameNotFoundException e) {
log.error("User " + username + " not found in ldap", e)
}
Collection<GrantedAuthority> authorities = new ArrayList<>()
for (String authority : currentLdapUserDetails.authorities) {
authorities.add(new SimpleGrantedAuthority(authority))
}
return authorities
}
#Override
public boolean supports(Class<?> authentication) {
return (OrbisAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
Resources.groovy
import edu.neu.security.OrbisAuthenticationFilter
import edu.neu.security.OrbisAuthenticationProvider
beans = {
userDetailsService(edu.neu.security.LDAPUserDetailsService)
orbisAuthenticationProvider(OrbisAuthenticationProvider)
orbisAuthenticationFilter(OrbisAuthenticationFilter) {
orbisAuthenticationProvider = ref("orbisAuthenticationProvider")
requiresAuthenticationRequestMatcher = ref('filterProcessUrlRequestMatcher')
// This throws error during startup. Unable to init bean
// authenicationManager = ref("authenicationManager")
}
myOAuth2ProviderFilter(OAuth2ProviderFilters) {
//grailsApplication = ref('grailsApplication')
// properties
}
}
I followed some of the concepts from this project: https://github.com/ppazos/cabolabs-ehrserver/
Even if the whole process is executed and securityContext is set with authenticated, when I hit oauth/authorize to get Authorization_Code, it redirects back to '/login/auth'. It still doesn't know that a user is already authenticated.
When you add an authentication provider to the AuthenticationManagerBuilder bean (which comes from AuthenticationConfiguration), the authentication manager bean you declare is not used.
Try:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled=true)
public class CustomWebSecurityConfig {
OrbisAuthenticationProvider lwoAuthProvider;
public CustomWebSecurityConfig() {
//
}
#Bean(name= BeanIds.AUTHENTICATION_MANAGER)
AuthenticationManager authenticationManagerBean() throws Exception {
return new ProviderManager(Arrays.asList(lwoAuthProvider));
}
Your AuthenticationManager bean should get picked up and will be used for method security. You can also #Autowire it in your filter if it is being managed by Spring, or #Autowire it in the #Configuration class that instantiates your filter.
NOTE: the above class WILL NOT create any of the Spring Security filters.
(The filter chain wasn't being created anyway - you didn't annotate your class with #EnableWebSecurity)

Spring TestRestTemplate authentication

I am trying to build Spring Boot test to test rest API, so that I can get Principal from the request and use that to identify the user.
Server returns
{"timestamp":1502014507361,"status":403,"error":"Forbidden","message":"Access
Denied","path":"/hello"}
What am I missing here?
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RestTemplateTest {
#Autowired
TestRestTemplate testRestTemplate;
#Test
public void testit() {
testRestTemplate.withBasicAuth("user", "password");
String responsePayload = testRestTemplate.getForObject("/hello", String.class);
}
#RestController
public class GreetingController {
#RequestMapping("/hello")
public String heipat(Principal principal) {
String string = "hello there";
return string;
}
#Configuration
#EnableWebSecurity
static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().anyRequest().hasRole("USER");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
You need to be authenticated first. like requesting a /login API.
Also you need to make the login API accessible by everyone by doing this:
http.csrf().disable().authorizeRequests()
.antMatchers("/login").permitAll()
When you includes WebSecurityConfig you will have basic usernamerAndPassowrd authentication.

How to use BCryptPasswordEncoder on account registration and on account authentication in spring?

I have created a web application based on Spring-Boot 1.2.2.RELEASE. I also have a database table where I manage user credentials. It has the basic columns e.g. id, username, and password. Also added salt column where I plan to store randomly generated salts per account.
I'm trying to use Spring Security and having some difficulty with authentication using the encoded password. So basically I have a Register Controller:
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<User> register(#RequestParam(value = "email_address") String emailAddress, #RequestParam(value = "password") String password) {
String username = emailAddress;
String salt = KeyGenerators.string().generateKey();
User user = new User();
user.setUsername(emailAddress);
user.setEmailAddress(emailAddress);
user.setSalt(salt);
user.setPassword(passwordEncoder.encode(username+salt+password));
user.setCreatedBy(1);
user.setCreatedOn(new Date());
user.setLastUpdatedby(1);
user.setLastUpdatedOn(new Date());
user.setStartDate(new Date());
return new ResponseEntity<User>(this.userRepository.save(user), HttpStatus.OK);
}
Then I implemented UserDetailsService like this:
#Service
protected static class ApplicationUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
return null;
}
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
String password = passwordEncoder.encode(username+user.getSalt()+user.getPassword());
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
}
However, since BCryptPasswordEncoder generates new result everytime even if the input is the same, how will it be able to authenticate?
EDIT:
Just wanted to add that if I don't use any encoder and store the password as plain text, I am able to authenticate just fine. But of course that's not what I want to do.
UPDATE 1
Here's config for WebSecurityConfigurerAdapter. Inside it I have the configure method.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationUserDetailsService applicationUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService).passwordEncoder(passwordEncoder);
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
UPDATE 2
I got it working by extending DaoAuthenticationProvider. See updated code below.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationUserDetailsService applicationUserDetailsService;
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(applicationUserDetailsService);
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
Also had to modify my RegisterController as shown below.
#RestController
public class RegisterController {
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<User> register(#RequestParam(value = "email_address") String emailAddress, #RequestParam(value = "password") String password) {
User user = new User();
user.setUsername(emailAddress);
user.setEmailAddress(emailAddress);
user.setPassword(passwordEncoder.encode(password));
user.setSalt(KeyGenerators.string().generateKey());
user.setCreatedBy(1);
user.setCreatedOn(new Date());
user.setLastUpdatedby(1);
user.setLastUpdatedOn(new Date());
user.setStartDate(new Date());
return new ResponseEntity<User>(this.userRepository.save(user), HttpStatus.OK);
}
}
UPDATE 3
Forgot to include the updated UserDetailService implementation.
#Service
protected static class ApplicationUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
return null;
}
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
String password = passwordEncoder.encode(user.getPassword());
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
}
I might need to remove the salt column now since it's basically useless.
Hopefully this helps others as well.