How to authenticate a user in spring boot? [closed] - spring-boot

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm not understanding how this code works especially the OncePerRequestFilter class what's the purpose of this class I''ve pasted the code available to me.
public class AuthenticationFilter extends OncePerRequestFilter{
private final LoginService loginService;
private static final Logger logger = Logger.getLogger(AuthenticationFilter.class);
public AuthenticationFilter(final LoginService loginService) {
super();
this.loginService = loginService;
}
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
final String xAuth = request.getHeader("X-Authorization");

Authenticate and Authorization are two different terms.
1. Authenticate : you are the one who you are claiming.
2. Authorization : What are you allowed to do.
Assumption : your question is for authorize : " I want to authorize specific user based on rest api's".
configure http.authorizeRequests().antMatchers("/products").access("hasRole('ROLE_ADMIN')")
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/products").access("hasRole('ROLE_ADMIN')").anyRequest().permitAll().and().authorizeRequests().antMatchers("/hello").access("hasRole('ROLE_ADMIN')").anyRequest().permitAll().and()
.formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password").and()
.logout().logoutSuccessUrl("/login?logout").and().exceptionHandling().accessDeniedPage("/403").and()
.csrf();
}
refer for full code : https://github.com/Roshanmutha/SpringSecurityJDBC/blob/master/src/main/java/com/roshantest/WebSecurityConfig.java

Related

Spring how create filter that reads body but keeps request intact [duplicate]

This question already has answers here:
How to get request body params in spring filter?
(2 answers)
Closed 4 months ago.
I need to calculate a value for every request body (soap requests) for this i created a filter (extending OncePerRequestFilter):
#Component
#RequiredArgsConstructor
public class AddHashFilter extends OncePerRequestFilter {
private final RequestHash hash;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if(!(request instanceof ContentCachingRequestWrapper)){
requestToUse = new ContentCachingRequestWrapper(request);
}
hash.hash(IOUtils.toString(requestToUse.getInputStream(), StandardCharsets.UTF_8));
filterChain.doFilter(requestToUse, response);
}
}
The problem is that this somehow destroys the request - i get 400. What i tried:
using request.geReader -> get an exception that getReader was already called
omit using ContentCachingRequestWrapper -> no change
i also tried this variant to read the body (from examples i found)
#Component
#RequiredArgsConstructor
public class AddHashFilter extends OncePerRequestFilter {
private final RequestHash hash;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
hash.hash(new String(StreamUtils.copyToByteArray(request.getInputStream()), StandardCharsets.UTF_8));
filterChain.doFilter(request, response);
}
}
The problem is same: all request are quit with 400.
But if i remove the actual work/ use of this filter (only keeping filterChain.doFilter...) it is working.
So how can i read complete body and keep it usable for everything after?
Http request could be read only once, so if you read it in filter you can not use it again. Spring provides its own class that extends HttpServletRequest and allows reading its contents multiple times. And that resolves your problem. See this question and my answer to it: How to get request body params in spring filter?

spring boot restful service disable Chrome login popup

I am building a restful web service with an Angular front end. If the users log in with wrong credentials, the Chrome browser automatically shows a login popup. I want to prevent this by responding to code 403 instead of 401.
i have review [this question][1] and it does not help with my use case. I have also tried different solutions found on google, but in all cases is stays giving 401 with the login popup.
With the newest Spring versions, the configuration to handle authentication has changed. For this reason most tutorials do not work anymore. here is the working configuration:
SecurityConfig.java:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//....
//.and()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint)
.and().csrf().disable();
}
}
CustomAuthenticationAntryPoint.java:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.debug("Pre-authenticated entry point called. Rejecting access");
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad credentials");
}
}

Add custom UserDetailsService to Spring Security OAuth2 app

How do I add the custom UserDetailsService below to this Spring OAuth2 sample?
The default user with default password is defined in the application.properties file of the authserver app.
However, I would like to add the following custom UserDetailsService to the demo package of the authserver app for testing purposes:
package demo;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
#Service
class Users implements UserDetailsManager {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password;
List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
if (username.equals("Samwise")) {
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "TheShire";
}
else if (username.equals("Frodo")){
auth = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_HOBBIT");
password = "MyRing";
}
else{throw new UsernameNotFoundException("Username was not found. ");}
return new org.springframework.security.core.userdetails.User(username, password, auth);
}
#Override
public void createUser(UserDetails user) {// TODO Auto-generated method stub
}
#Override
public void updateUser(UserDetails user) {// TODO Auto-generated method stub
}
#Override
public void deleteUser(String username) {// TODO Auto-generated method stub
}
#Override
public void changePassword(String oldPassword, String newPassword) {
// TODO Auto-generated method stub
}
#Override
public boolean userExists(String username) {
// TODO Auto-generated method stub
return false;
}
}
As you can see, this UserDetailsService is not autowired yet, and it purposely uses insecure passwords because it is only designed for testing purposes.
What specific changes need to be made to the GitHub sample app so that a user can login as username=Samwise with password=TheShire, or as username=Frodo with password=MyRing? Do changes need to be made to AuthserverApplication.java or elsewhere?
SUGGESTIONS:
The Spring OAuth2 Developer Guide says to use a GlobalAuthenticationManagerConfigurer to configure a UserDetailsService globally. However, googling those names produces less than helpful results.
Also, a different app that uses internal spring security INSTEAD OF OAuth uses the following syntax to hook up the UserDetailsService, but I am not sure how to adjust its syntax to the current OP:
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
protected static class AuthenticationSecurity extends GlobalAuthenticationConfigurerAdapter {
#Autowired
private Users users;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
I tried using #Autowire inside the OAuth2AuthorizationConfig to connect Users to the AuthorizationServerEndpointsConfigurer as follows:
#Autowired//THIS IS A TEST
private Users users;//THIS IS A TEST
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(users)//DetailsService)//THIS LINE IS A TEST
;
}
But the Spring Boot logs indicate that the user Samwise was not found, which means that the UserDetailsService was not successfully hooked up. Here is the relevant excerpt from the Spring Boot logs:
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9] o.s.s.a.dao.DaoAuthenticationProvider :
User 'Samwise' not found
2016-04-20 15:34:39.998 DEBUG 5535 --- [nio-9999-exec-9]
w.a.UsernamePasswordAuthenticationFilter :
Authentication request failed:
org.springframework.security.authentication.BadCredentialsException:
Bad credentials
What else can I try?
I ran into a similar issue when developing my oauth server with Spring Security. My situation was slightly different, as I wanted to add a UserDetailsService to authenticate refresh tokens, but I think my solution will help you as well.
Like you, I first tried specifying the UserDetailsService using the AuthorizationServerEndpointsConfigurer, but this does not work. I'm not sure if this is a bug or by design, but the UserDetailsService needs to be set in the AuthenticationManager in order for the various oauth2 classes to find it. This worked for me:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Users userDetailsService;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// other stuff to configure your security
}
}
I think if you changed the following starting at line 73, it may work for you:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
You would also of course need to add #Autowired Users userDetailsService; somewhere in WebSecurityConfigurerAdapter
Other things I wanted to mention:
This may be version specific, I'm on spring-security-oauth2 2.0.12
I can't cite any sources for why this is the way it is, I'm not even sure if my solution is a real solution or a hack.
The GlobalAuthenticationManagerConfigurer referred to in the guide is almost certainly a typo, I can't find that string anywhere in the source code for anything in Spring.
My requirement was to get a database object off the back of the oauth2 email attribute. I found this question as I assumed that I need to create a custom user details service. Actually I need to implment the OidcUser interface and hook into that process.
Initially I thought it was the OAuth2UserService, but I've set up my AWS Cognito authentication provider so that it's open id connect..
//inside WebSecurityConfigurerAdapter
http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(new CustomOidcUserServiceImpl());
...
public class CustomOidcUserServiceImpl implements OAuth2UserService<OidcUserRequest, OidcUser> {
private OidcUserService oidcUserService = new OidcUserService();
#Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = oidcUserService.loadUser(userRequest);
return new CustomUserPrincipal(oidcUser);
}
}
...
public class CustomUserPrincipal implements OidcUser {
private OidcUser oidcUser;
//forward all calls onto the included oidcUser
}
The custom service is where any bespoke logic can go.
I plan on implementing UserDetails interface on my CustomUserPrincipal so that I can have different authentication mechanisms for live and test to facilitate automated ui testing.
I ran into the same issue and originally had the same solution as Manan Mehta posted. Just recently, some version combination of spring security and spring oauth2 resulted in any attempt to refresh tokens resulting in an HTTP 500 error stating that UserDetailsService is required in my logs.
The relevant stack trace looks like:
java.lang.IllegalStateException: UserDetailsService is required.
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:463)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)
You can see at the bottom that the DefaultTokenServices is attempting to refresh the token. It then calls into an AuthenticationManager to re-authenticate (in case the user revoked permission or the user was deleted, etc.) but this is where it all unravels. You see at the top of the stack trace that UserDetailsServiceDelegator is what gets the call to loadUserByUsername instead of my beautiful UserDetailsService. Even though inside my WebSecurityConfigurerAdapter I set the UserDetailsService, there are two other WebSecurityConfigurerAdapters. One for the ResourceServerConfiguration and one for the AuthorizationServerSecurityConfiguration and those configurations never get the UserDetailsService that I set.
In tracing all the way through Spring Security to piece together what is going on, I found that there is a "local" AuthenticationManagerBuilder and a "global" AuthenticationManagerBuilder and we need to set it on the global version in order to have this information passed to these other builder contexts.
So, the solution I came up with was to get the "global" version in the same way the other contexts were getting the global version. Inside my WebSecurityConfigurerAdapter I had the following:
#Autowired
public void setApplicationContext(ApplicationContext context) {
super.setApplicationContext(context);
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
try {
globalAuthBuilder.userDetailsService(userDetailsService);
} catch (Exception e) {
e.printStackTrace();
}
}
And this worked. Other contexts now had my UserDetailsService. I leave this here for any brave soldiers who stumble upon this minefield in the future.
For anyone got UserDetailsService is required error when doing refresh token, and you confirm you already have UserDetailsService bean.
try add this:
#Configuration
public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
private UserDetailsService userDetailsService;
public GlobalSecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
This is my try and error, maybe not work for you.
By the way, if you give up "guess" which bean will pick by spring, you can extends AuthorizationServerConfigurerAdapter and WebSecurityConfigurerAdapter and config all stuff by yourself, but I think it's lose power of spring autoconfig.
Why I need config everything if I just need customize some config?

Token Based Authorization for REST services using Spring Security+Spring Boot

I am building REST services(JAVA/SPRING) for mobile applications in which have to take care of Authentication and Authorization. For Authentication I am using external tool but for role based Authorization I want to use Spring Security. The project uses Spring Boot +Spring Data JPA+ Spring REST.
I did a sample Project to get some hands on Spring Security but after spending few hours I was able to make it work some how, but have specific doubts.
Few classes from Sample:-
#Configuration
#EnableWebSecurity
#ComponentScan("com.ezetap.security")
#EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired DataSource dataSource;
#Autowired CustomAuthenticationProvider authenticationProvider;
#Autowired CustomerUserDetailService customerUserDetailService;//Overrides loadByUserName method
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService(customerUserDetailService);
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET,"/spring-security/test/**").hasRole("USER");
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
http.addFilterBefore(new RestAuthenticationFilter(authenticationProvider,customerUserDetailService), BasicAuthenticationFilter.class);
}
}
public class RestAuthenticationFilter extends GenericFilterBean {
CustomerUserDetailService authenticationService;
AuthenticationProvider authenticationProvider;
public RestAuthenticationFilter() {
}
public RestAuthenticationFilter(CustomerUserDetailService customerUserDetailService) {
this.authenticationService=customerUserDetailService;
}
public RestAuthenticationFilter(AuthenticationProvider authenticationProvider,
CustomerUserDetailService customerUserDetailService) {
this.authenticationProvider=authenticationProvider;
this.authenticationService=customerUserDetailService;
}
public final String HEADER_SECURITY_TOKEN = "X-CustomToken";
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if("test".equalsIgnoreCase(request.getHeader(HEADER_SECURITY_TOKEN))){
UserDetails userDetails=authenticationService.loadUserByUsername("");//assume this is working
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}else{
response.sendError(401, "Authorization failed");
//response.getWriter().append("Access denied");
}
} <br>
I have to use Token based Authorization in the Header. What will be the correct way to Generate tokens and then encrypt and send to Browser which then be used with all requests.
Is their any service where Spring generates a Token and persists or I have to do it manually and generate a token and store it somewhere and then evict it periodically?
I should also be able to get all tokens or invalidate a particular token or some other token related services.
Also, please let me know if the approach I am following is correct or if some other thing needs to be considered.This is purely for REST services, so no state or session. Dont want to use Spring Oauth due to complexity and time constraints.

Add Maintenance Mode to Spring (Security) app

I'm looking for a way to implement a Maintenance Mode in my Spring app.
While the app is in Maintenance Mode only users role = MAINTENANCE should be allowed to log in. Everyone else gets redirected to login page.
Right now I just built a Filter:
#Component
public class MaintenanceFilter extends GenericFilterBean {
#Autowired SettingStore settings;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(settingStore.get(MaintenanceMode.KEY).isEnabled()) {
HttpServletResponse res = (HttpServletResponse) response;
res.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} else {
chain.doFilter(request, response);
}
}
}
And added it using:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// omitted other stuff
.addFilterAfter(maintenanceFilter, SwitchUserFilter.class);
}
Because as far as I figured out SwitchUserFilter should be the last filter in Spring Security's filter chain.
Now every request gets canceled with a 503 response. Though there's no way to access the login page.
If I add a redirect to the Filter, this will result in an infinite loop, because access to login page is also denied.
Additionally I can't figure out a nice way to get the current users roles. Or should I just go with SecurityContextHolder ?
I'm looking for a way to redirect every user to the login page (maybe with a query param ?maintenance=true) and every user with role = MAINTENANCE can use the application.
So the Filter / Interceptor should behave like:
if(maintenance.isEnabled()) {
if(currentUser.hasRole(MAINTENANCE)) {
// this filter does nothing
} else {
redirectTo(loginPage?maintenance=true);
}
}
I now found two similar solutions which work, but the place where I inject the code doesn't look that nice.
For both I add a custom RequestMatcher, which get's #Autowired and checks if Maintenance Mode is enabled or not.
Solution 1:
#Component
public class MaintenanceRequestMatcher implements RequestMatcher {
#Autowired SettingStore settingStore;
#Override
public boolean matches(HttpServletRequest request) {
return settingStore.get(MaintenanceMode.KEY).isEnabled()
}
}
And in my Security Config:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired MaintenanceRequestMatcher maintenanceRequestMatcher;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.requestMatchers(maintenanceRequestMatcher).hasAuthority("MY_ROLE")
.anyRequest().authenticated()
// ...
}
Solution 2:
Very similar, but uses HttpServletRequest.isUserInRole(...):
#Component
public class MaintenanceRequestMatcher implements RequestMatcher {
#Autowired SettingStore settingStore;
#Override
public boolean matches(HttpServletRequest request) {
return settingStore.get(MaintenanceMode.KEY).isEnabled() && !request.isUserInRole("MY_ROLE");
}
}
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired MaintenanceRequestMatcher maintenanceRequestMatcher;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.requestMatchers(maintenanceRequestMatcher).denyAll()
.anyRequest().authenticated()
// ...
}
This will perform a denyAll() if Maintenance Mode is enabled and the current user does not have MY_ROLE.
The only disadvantage is, that I cannot set a custom response. I'd prefer to return a 503 Service Unavailable. Maybe someone can figure out how to do this.
It's kinda of a chicken or egg dilemma, you want to show unauthorized users a "we're in maintenance mode ..." message, while allow authorized users to login, but you don't know if they are authorized until they log in. Ideally it would be nice to have this in some sort of filter, but I found in practice it was easier for me to solve a similar issue by putting the logic after login, like in the UserDetailsService.
Here's how I solved it on a project. When I'm in maintenance mode, I set a flag for the view to show the "we're in maintenance mode .." message, in a global header or on the login page. So users, regardless of who they are know it's maintenance mode. Login should work as normal.
After user is authenticated, and in my custom UserDetailsService, where their user details are loaded with their roles, I do the following:
// if we're in maintenance mode and does not have correct role
if(maintenance.isEnabled() && !loadedUser.hasRole(MAINTENANCE)) {
throw new UnauthorizedException(..)
}
// else continue as normal
It's not pretty, but it was simple to understand (which I think is good for security configuration stuff) and it works.
Update:
With you solution I'd have to destroy everyone's session, else a user
which was logged in before maintenance mode was enabled, is still able
to work with the system
On our project we don't allow any users to be logged in while in maintenance mode. An admin, kicks off a task which enables "maintenance..." msg, with a count down, then at the end, we expire everyone's session using SessionRegistry.
I was a similar situation and found this answer is helpful. I followed the second approach and also managed to return custom response.
Here is what I have done to return the custom response.
1- Define a controller method that returns the needed custom response.
#RestController
public class CustomAccessDeniedController {
#GetMapping("/access-denied")
public String getAccessDeniedResponse() {
return "my-access-denied-page";
}
}
2- In your security context, you should permit this URL to be accessible.
http.authorizeRequests().antMatchers("/access-denied").permitAll()
3- Create a custom access denied exception handler
#Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Autowired
private SettingStore settingStore;
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
if(settingStore.get(MaintenanceMode.KEY).isEnabled()) {
response.sendRedirect(request.getContextPath() + "/access-denied");
}
}
}
4- Register the custom access denier exception handler in the security config
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

Resources