Java spring boot basic authentication with redis - java

I'm trying to add authentication layer into my spring boot application, and all the tutorials i can find are with some mock user, such as:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("learn").password("share").roles("USER");
}
I would like to implement something like this:
String credentials = request.getHeader('Authorization');
String userName = fetchUserName(credentials);
String password = fetchUserPassword(credentials);
String actualPassword = redis.getPassowrdForUser(userName);
if (actualPassword == someHash(password)) {
// continue with the request
} else {
// return unauthorised request 401
}
Thanks.
This is my current implementation, from some tutorial i've found:
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private BasicAuthenticationPoint authEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/signup").permitAll()
.anyRequest().authenticated();
http.httpBasic().authenticationEntryPoint(authEntryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("learn").password("share").roles("USER");
}
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
#Component
public class BasicAuthenticationPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("learn");
super.afterPropertiesSet();
}
}

Related

Spring boot securing api with both basic authorization and jwt role based authorization

I am trying to secure my api requests with both basic authorization and jwt role based authorization.
I have two classes for basic auth and web security config. Both jwt role based auth and basic auth classed are imported in WebSecurityConfigurerAdapter.
When running the application, api is working only with basic auth and does not know jwt token included or not.
WebSecurityConfigurerAdapter class
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Autowired
public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
super();
this.userDetailsService = userDetailsService;
}
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.authorizeRequests()
.antMatchers("/actuator/health", "/api/auth/signup", "/api/auth/login", "/api/auth/logout").permitAll()
.antMatchers("/api/test/public").permitAll()
.antMatchers("/api/test/user").hasAnyAuthority(UserLoginRole.USER.value())
.anyRequest().authenticated()
.and()
.formLogin().disable()
.csrf().disable()
.httpBasic().realmName(REALM)
.authenticationEntryPoint(getBasicAuthEntryPoint());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.cors();
}
#Bean
public BasicAuthentication getBasicAuthEntryPoint() {
return new BasicAuthentication();
}
}
BasicAuthentication class
public class BasicAuthentication extends BasicAuthenticationEntryPoint {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 : " + authException.getMessage());
}
#Override
public void afterPropertiesSet() {
setRealmName(REALM);
super.afterPropertiesSet();
}
}
Requests did not pass through authorization jwt token included or not.
What am I missing here?

Spring-Security Authorization Roles are not working

Trying to implement Spring Security based role authorizations in my existing Spring Boot system. I want to restrict access of REST API's as per some user authorizations.
For that I created an SpringConfiguration.java class as below:
SecurityConfiguration.java
public class SecurityConfig_BasicAuth extends WebSecurityConfigurerAdapter {
private static String REALM = "Test";
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("Deb").password("deb#123").roles("ADMIN"); // Line 1
auth.inMemoryAuthentication().withUser("Bob").password("Bob#123").roles("USER"); // Line 2
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().antMatchers("/checkRoleBaseAuthorization/**")
.hasRole("ADMIN").and() // Line 3
.httpBasic().realmName(REALM)
.authenticationEntryPoint(getBasicAuthEntryPoint()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint() {
return new CustomBasicAuthenticationEntryPoint();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
}
In the above class, I have defined two users/passwords with authorization roles ADMIN/USER. Now I want to allow only those users that have ADMIN roles.
In case credentials do not match below class will return status 401.
CustomBasicAuthenticationEntryPoint.java
public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 : " + authException.getMessage());
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("Test");
super.afterPropertiesSet();
}
}
REST API
#RequestMapping(value = "/checkRoleBaseAuthorization", method = RequestMethod.GET)
public String checkRoleBaseAuthorization() {
return "checkRoleBaseAuthorization() Invoked";
}
My program is working to an extent i.e. without users/passwords the API is not accessible.
But why I'm able to login with Bob/Bob#123 too? It has role "USER",
not "ADMIN" which I have specified in SecurityConfiguration.java (see
line 3).

Spring Security java configuration confusion

Why the login isn't prompted with following configuration? When I try to access /public/user, I get error 403 (access denied). However, if I uncomment those commented lines at WebServiceSecurityConfiguration.configure, I got redirected to login page, as desired. Why those lines are needed for from-login being properly configured, as the antMatcher matches different path in the first place. I guess there is some conflict, which misconfigures the AuthenticationEntryPoint, but I don't really have idea how that happens. What I'm trying to achieve is configuring two security chains, one for login path to obtain the JWT token, and another for web services to authenticate against the token. Everything works perfectly with those lines uncommented, but I noticed by accident form-login stopped working without them, and am super confused why is that.
#Configuration
#Profile("javasecurity")
#Order(11)
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private TokenHandler tokenHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").authorities(new SimpleGrantedAuthority("ROLE_USER")).and()
.withUser("admin").password("password").authorities(
new SimpleGrantedAuthority("ROLE_USER"),
new SimpleGrantedAuthority("ROLE_ADMIN")).and()
.withUser("guest").password("guest").authorities(new SimpleGrantedAuthority("ROLE_GUEST"));
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**")
.permitAll()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.and()
.logout();
}
#Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AuthenticationSuccessHandler() {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
tokenHandler.setToken(response, authentication.getName());
response.getWriter().println("User authenticated and cookie sent");
response.flushBuffer();
}
};
}
#Configuration
#Profile("javasecurity")
#Order(10)
public static class WebServiceSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private TestAuthenticationFilter testAuthenticationFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secured/**")
.authenticated();
// .and()
// .antMatcher("/secured/**")
// .securityContext().securityContextRepository(new NullSecurityContextRepository())
// .and()
// .addFilterAt(testAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
--
#Component("TestAuthenticationFilter")
public class TestAuthenticationFilter extends GenericFilterBean {
#Autowired
private TokenHandler tokenHandler;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("TestAuthenticationFilter doFitler");
attemptAuthentication((HttpServletRequest) request);
chain.doFilter(request, response);
clearAuthentication();
System.out.println("doFitler end");
}
public void attemptAuthentication(HttpServletRequest request) {
try {
UserDetails user = tokenHandler.loadUserFromToken(request);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, user.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
// Do nothing
}
}
public void clearAuthentication() {
SecurityContextHolder.getContext().setAuthentication(null);
}
#Configuration
public static class DisableFilterRegistration {
#Autowired
private TestAuthenticationFilter filter;
#Bean
public FilterRegistrationBean disablerBean() {
FilterRegistrationBean bean = new FilterRegistrationBean(filter);
bean.setEnabled(false);
return bean;
}
}
}
--
#Component("TokenHandler")
public class TokenHandler {
#Autowired(required = false)
private UserDetailsService userDetailsService;
public void setToken(HttpServletResponse response, String username) {
response.addCookie(new Cookie("user", username));
}
public UserDetails loadUserFromToken(HttpServletRequest request) throws BadCredentialsException {
Cookie[] cookies = request.getCookies();
Cookie token = null;
for (Cookie c : cookies) {
if (c.getName().equals("user")) {
token = c;
break;
}
}
if (token == null)
return null;
else
return userDetailsService.loadUserByUsername(token.getValue());
}
}
--
#RestController
#RequestMapping("/public")
public class PublicController {
#GetMapping("/norole")
public String noRole() {
return "no role";
}
#GetMapping("/user")
#PreAuthorize("hasRole('ROLE_USER')")
public String roleUser() {
return "role_user";
}
}
--
#RestController
#RequestMapping("/secured")
public class SecuredController {
#GetMapping("/user")
#PreAuthorize("hasRole('ROLE_USER')")
public String roleUser() {
return "role_user";
}
#GetMapping("/admin")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public String roleAdmin() {
return "role_admin";
}
#GetMapping("/norole")
public String noRole() {
return "no role";
}
}
Login-from got functional again after declaring adding
http.antMatcher("/secured/**")
As the first call in the WebServiceSecurityConfiguration.configure. Does that mean that without it the configuration negates the form-login, which is configured after this particular configuration? Also, it seems like the position of antMatcher can be arbitrary, is this the case? Could someone explain what is actually happening there?

Why does this token based Spring Security filter not get called?

I am currently working on a project in which I use Angular JS and Spring REST services. These last few days I've been trying to get some security into the system (see my previous post). I'm implementing token based security.
I got the basic stuff working, but the XAuthTokenFilter doesn't get called when requests are being done. I have no idea why, I think it's something very simple that I'm overlooking. The relevant classes:
XAuthTokenFilter (doFilter does not get called each request)
public class XAuthTokenFilter extends GenericFilterBean {
private final static String XAUTH_TOKEN_HEADER_NAME = "x-auth-token";
private UserDetailsService detailsService;
private TokenProvider tokenProvider;
public XAuthTokenFilter(UserDetailsService detailsService, TokenProvider tokenProvider) {
this.detailsService = detailsService;
this.tokenProvider = tokenProvider;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String authToken = httpServletRequest.getHeader(XAUTH_TOKEN_HEADER_NAME);
if (StringUtils.hasText(authToken)) {
String username = this.tokenProvider.getUserNameFromToken(authToken);
UserDetails details = this.detailsService.loadUserByUsername(username);
if (this.tokenProvider.validateToken(authToken, details)) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(details, details.getPassword(), details.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
}
}
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
XAuthTokenConfigurer
public class XAuthTokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private TokenProvider tokenProvider;
private UserDetailsService detailsService;
public XAuthTokenConfigurer(UserDetailsService detailsService, TokenProvider tokenProvider) {
this.detailsService = detailsService;
this.tokenProvider = tokenProvider;
}
#Override
public void configure(HttpSecurity http) throws Exception {
XAuthTokenFilter customFilter = new XAuthTokenFilter(detailsService, tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
SecurityConfiguration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private UserDetailsService userDetailsService;
#Inject
private TokenProvider tokenProvider;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/scripts/**/*.{js,html}");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/protected/**").authenticated()
.antMatchers("/api/open/**").permitAll()
.and()
.apply(securityConfigurerAdapter());
}
private XAuthTokenConfigurer securityConfigurerAdapter() {
return new XAuthTokenConfigurer(userDetailsService, tokenProvider);
}
/**
* This allows SpEL support in Spring Data JPA #Query definitions.
*
* See https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions
*/
#Bean
EvaluationContextExtension securityExtension() {
return new EvaluationContextExtensionSupport() {
#Override
public String getExtensionId() {
return "security";
}
#Override
public SecurityExpressionRoot getRootObject() {
return new SecurityExpressionRoot(SecurityContextHolder.getContext().getAuthentication()) {};
}
};
}
}
I really have no clue why it doesn't get called, is there something wrong with my url antMatcher() statements?
My Context which might be good to include:
#Configuration
#EnableWebMvc
#Import(AppContext.class) // The context from my backend which is included as a dependency
#ComponentScan("com.example.springsecuritytest")
public class RestContext extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
WebAppInitializer
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
//RootContext
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RestContext.class);
// Add RootContext using ContextLoaderListener
servletContext.addListener(new ContextLoaderListener(rootContext));
// Registering and mapping dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Please check out this git repo. I have prepared a very basic setup that test security with and without a tokenAuthenticationFilter. The filter implementation is just a mock, setting a valid Authentication whenever the header is present, regardless of its value. Please also note the two application WebApplicationInitializers, which are Servlet 3.0 conformant and are configuring the Servlet container programmatically (instead of web.xml).

Spring Security: AuthenticationProcessingFilter is called twice

I try configure Spring Security via token authorization in RESTful application.
My AuthenticationFilter looks like:
#Configurable
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomTokenAuthenticationFilter.class);
private final static String SECRET_KEY = "ThisIsASecretKey";
public final String HEADER_SECURITY_TOKEN = "X-Token";
#Inject
private Users usres;
public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(new NoOpAuthenticationManager());
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
String token = request.getHeader(HEADER_SECURITY_TOKEN);
logger.info("token found:" + token);
TokenInfo tokenInfo = new TokenInfo(token, SECRET_KEY);
AbstractAuthenticationToken userAuthenticationToken;
try {
userAuthenticationToken = authUserByToken(tokenInfo);
if (userAuthenticationToken == null)
throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
return userAuthenticationToken;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private AbstractAuthenticationToken authUserByToken(TokenInfo token) throws ParseException {
if (token == null) {
return null;
}
UserInfo userInfo = usres.findUser(token.getUsername());
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setProvider(new UserProvider());
User userDetails = mapper.map(userInfo, User.class);
AbstractAuthenticationToken authToken = new AuthenticationToken(userDetails);
try {
return authToken;
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return authToken;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
chain.doFilter(request, response);
}
});
super.doFilter(req, res, chain);
}
}
and Spring Security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Inject
AuthenticationManager authenticationManager;
#Bean
protected AbstractAuthenticationProcessingFilter getTokenAuthFilter() throws Exception {
CustomTokenAuthenticationFilter tapf = new CustomTokenAuthenticationFilter("/api/secure-module/admin/**");
tapf.setAuthenticationManager(authenticationManager);
return tapf;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable().addFilterBefore(getTokenAuthFilter(), AnonymousAuthenticationFilter.class).exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint());
}
}
It works fine but CustomTokenAuthenticationFilter is called twice and I don't know why. Any ideas?
I found problem, it is #Bean annotation in getTokenAuthFilter method. Then I had 2 registered filter in chain (additionalFilters, originalChain).
I had a similar experience when the Filter was generating an exception causing a redirect to /error which triggered the Filter again. I had to specify
#Override
public void configure(WebSecurity web) throws Exception {
// ignoring security for /error
web.ignoring().antMatchers("/error");
}

Categories