Spring Security Token based Authentication - java

I have a rest api where I am authenticating using spring security Basic Authorization where client sends username and password for each request.
Now, I wanted to implement token based authentication where I will send a token in response header when user is authenticated at first. For further requests, client can include that token in the header which will be used to authenticate the user to the resources. I have two authentication providers tokenAuthenticationProvider and daoAuthenticationProvider
#Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
#Autowired
private TokenAuthentcationService service;
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
final String token = request.getHeader(Constants.AUTH_HEADER_NAME);
final Token tokenObj = this.service.getToken(token);
final AuthenticationToken authToken = new AuthenticationToken(tokenObj);
return authToken;
}
#Override
public boolean supports(final Class<?> authentication) {
return AuthenticationToken.class.isAssignableFrom(authentication);
}
}
And in daoAuthenticationProvider I am setting custom userDetailsService and authenticating against user login details by fetching it from the database (which is working fine as long as user name and password are passed using Authorization:Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag== as header)
But when I include token in the header using X-AUTH-TOKEN (which is Constants.AUTH_HEADER_NAME), tokenAuthenticationProvider is not being called. I am getting error as
{"timestamp":1487626368308,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/find"}
And here is how I am adding authentication providers.
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
final UsernamePasswordAuthenticationProvider daoProvider = new
UsernamePasswordAuthenticationProvider(this.service, this.passwordEncoder());
auth.authenticationProvider(this.tokenAuthenticationProvider);
auth.authenticationProvider(daoProvider);
}
Please suggest how can I implement Token based authentication without hurting the current behavior of spring security.

Here is how I was able to implement token based authentication and basic authentication
SpringSecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(this.participantService).passwordEncoder(this.passwordEncoder());
}
#Override
protected void configure(final HttpSecurity http) throws Exception
{
//Implementing Token based authentication in this filter
final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter();
http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class);
//Creating token when basic authentication is successful and the same token can be used to authenticate for further requests
final CustomBasicAuthenticationFilter customBasicAuthFilter = new CustomBasicAuthenticationFilter(this.authenticationManager() );
http.addFilter(customBasicAuthFilter);
}
}
TokenAuthenticationFilter.java
public class TokenAuthenticationFilter extends GenericFilterBean
{
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException
{
final HttpServletRequest httpRequest = (HttpServletRequest)request;
//extract token from header
final String accessToken = httpRequest.getHeader("header-name");
if (null != accessToken) {
//get and check whether token is valid ( from DB or file wherever you are storing the token)
//Populate SecurityContextHolder by fetching relevant information using token
final User user = new User(
"username",
"password",
true,
true,
true,
true,
authorities);
final UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
CustomBasicAuthenticationFilter.java
#Component
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {
#Autowired
public CustomBasicAuthenticationFilter(final AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void onSuccessfulAuthentication(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final Authentication authResult) {
//Generate Token
//Save the token for the logged in user
//send token in the response
response.setHeader("header-name" , "token");
}
}
As our CustomBasicAuthenticationFilter has been configured and added as a filter to the spring security,
Whenever basic authentication is successful the request will be redirected to onSuccessfulAuthentication where we set the token and send it in the response with some header "header-name".
If "header-name" is sent for further request, then the request will go through TokenAuthenticationFilter first before attempting to try Basic Authentication.

You can try setting your custom AuthenticationToken token in your authentication filter, for example:
public class AuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final String authTokenHeader = ((HttpServletRequest)request).getHeader(Constants.AUTH_HEADER_NAME);
if (authTokenHeader != null) {
SecurityContextHolder.getContext().setAuthentication(createAuthenticationToken(authTokenHeader));
}
chain.doFilter( request, response );
}
}

Related

Spring Security: redirect user based on request body

I have a scenario that based on the request body content, user should be allowed to access certain resource on SOAP services. I can't achieve this using antMatcher(**) because the path is same for all request.
I tried by adding a filter:
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest r = (HttpServletRequest)request;
MyRequestWrapper req = new MyRequestWrapper(r);
String body = req.getBody();
if(body.indexOf("searchKeyOnBody")!=0){
//Need to check if user has specified role or not
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Set<String> roles = authentication.getAuthorities().stream()
.map(r -> r.getAuthority()).collect(Collectors.toSet());
boolean hasManagerRole = authentication.getAuthorities().stream()
.anyMatch(r -> r.getAuthority().equals("ROLE_MANAGER"));
if(!hasManagerRole){
throwUnauthorized(response);
return;
}
}
chain.doFilter(req, response);
}
In spring security config:
#Configuration
public class MyAppConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.**.addFilterAfter(new MyFilter (), UsernamePasswordAuthenticationFilter.class)
The problem here is Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); in filter class is null. So, I am not able to retrive the user info and it's role.
Question:
Is there any way to retrieve the user info in the filter?
Anybody have better idea for this?

How to redirect my previous page after SSO Login in spring security?

How to redirect my previous page after SSO Login in spring security
I used set userReferer as true ,
But not able achieve it. please suggest some sample code or site.
Spring security with IDP we are using
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public LoginSuccessHandler() {
super();
setUseReferer(true);
}
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
/// some code
//set our response to OK status
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
String targetUrl = determineTargetUrl(authentication);
httpServletResponse.sendRedirect(targetUrl);
}
}
Once the user gets authenticated on IDP (Identity provider) side then SP (Service provider) receives assertions or response from IDP. That response would be validated on SP side. Upon response validation, this class OAuthUserLoginSuccessHandler would be called, where you can extract the information from the response provided by IDP and proceed with redirection as per the following code.
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
public class OAuthUserLoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
public OAuthUserLoginSuccessHandler() {}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
if (authentication.getPrincipal() instanceof UserDetails
|| authentication.getDetails() instanceof UserDetails) {
UserDetails details;
if (authentication.getPrincipal() instanceof UserDetails) {
details = (UserDetails) authentication.getPrincipal();
} else {
details = (UserDetails) authentication.getDetails();
}
String username = details.getUsername();
// get user info from datastore using username
// some code
String redirectUri; // get target uri either from relay state or from datastore
if (null != redirectUri) {
response.sendRedirect(redirectUri);
return;
}
}
super.onAuthenticationSuccess(request, response, authentication);
}
}

Spring security. Cannot permit request in custom fiter

I need to implement authorization with a specific header (say "sessionId") and secure all uri's except one.
I extended OncePerRequestFilter and implemented custom AuthenticationProvider to check if sessionId is valid (as well as custom Token class etc).
How it works now: for any uri it immediately jumps to AuthSessionAuthenticationProvider's authenticate method right after AuthSessionFilter is applied and returns 403 if header sessionId isn't specified. But I want some uri's to allow access without that header.
It all together:
config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(permittedUris).permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new AuthSessionAccessDeniedHandler())
.and().addFilterBefore(new AuthSessionFilter(), BasicAuthenticationFilter.class);
}
Filter:
public class AuthSessionFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication auth = new AuthSessionToken(request.getHeader("sessionId"));
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(request, response);
}
}
Provider:
public class AuthSessionAuthenticationProvider implements AuthenticationProvider {
//...
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AuthSessionToken token = (AuthSessionToken) authentication;
if (token.getSessionId() == null) {
throw new AccessDeniedException("Missing header sessionId");
}
AuthSessionAuthorities user = authSessionService.getUserAuthoritiesToken(token.getSessionId());
if (user == null) {
throw new AccessDeniedException("Session ID invalid: " + token.getSessionId());
}
token.setAuthenticatedUser(user);
return token;
}
//...
}
I found more elegant solution that was developed exactly for that purpose.
It's a RequestHeaderAuthenticationFilter. And then antMatchers works as expected. The initial configuration looks like this:
#Bean
#SneakyThrows
public RequestHeaderAuthenticationFilter preAuthenticationFilter() {
RequestHeaderAuthenticationFilter preAuthenticationFilter = new RequestHeaderAuthenticationFilter();
preAuthenticationFilter.setPrincipalRequestHeader(SESSION_ID);
preAuthenticationFilter.setCredentialsRequestHeader(SESSION_ID);
preAuthenticationFilter.setExceptionIfHeaderMissing(false);
preAuthenticationFilter.setContinueFilterChainOnUnsuccessfulAuthentication(true);
preAuthenticationFilter.setAuthenticationManager(authenticationManager());
return preAuthenticationFilter;
}

Understanding jwt and login logic

I am new to jwt and im using some resources from the net to help me get to understand jwt properly , I am able now to generate a token and access a resource that requires authorisation, first , i have a controller like
#RequestMapping("/token")
public class TokenController {
private JwtGenerator jwtGenerator;
public TokenController(JwtGenerator jwtGenerator) {
this.jwtGenerator = jwtGenerator;
}
#PostMapping
public String generate(#RequestBody final JwtUser jwtUser) {
return jwtGenerator.generate(jwtUser);
}
excuse me for i will be posting a lot of code.I am using postman for testing
so when i pass this as a post
{
"useNe" : "ter",
"paord":"123",
"role":"ain"
} or
{
"username" : "ter",
"is":"123",
"role":"admin"
}
I am generating a token ,it should require a username and password i think before a jwt token should be produced and i want to implement a proper login ,below is the security config and other classes that i have
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationProvider authenticationProvider;
#Autowired
private JwtAuthenticationEntryPoint entryPoint;
#Bean
public AuthenticationManager authenticationManagerBean() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilter() throws Exception {
JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();
filter.setAuthenticationManager(authenticationManager());
// we set success handler so that we overide the default redirection
filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("**/rest/**").authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(entryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
}
how can i create a login that will generate a token , or is that not the standard for jwt , also i want to have two types of role user and admin, admin can access all resources while user can access some , here are other classes
public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationTokenFilter() {
super("/rest/**");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
String header = httpServletRequest.getHeader("Authorisation");
if (header == null || !header.startsWith("Token")) {
throw new RuntimeException("JWT Token is missing");
}
String authenticationToken = header.substring(6);
JwtAuthenticationToken token = new JwtAuthenticationToken(authenticationToken);
return getAuthenticationManager().authenticate(token);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
And my jwt generator should this not require a an username and password for login
#Component
public class JwtGenerator {
public String generate(JwtUser jwtUser) {
Claims claims = Jwts.claims()
.setSubject(jwtUser.getUserName());
claims.put("userId", String.valueOf(jwtUser.getId()));
claims.put("role", jwtUser.getRole());
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, "youtube")
.compact();
}
}
#Component
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
#Autowired
private JwtValidator validator;
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException {
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException {
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) usernamePasswordAuthenticationToken;
String token = jwtAuthenticationToken.getToken();
JwtUser jwtUser = validator.validate(token);
if(jwtUser == null){
throw new RuntimeException("user token is incorrect" );
}
List<GrantedAuthority> grantedAuthorities = AuthorityUtils
.commaSeparatedStringToAuthorityList(jwtUser.getRole());
//we return an authenticated user
return new JwtUserDetails(jwtUser.getUserName(),jwtUser.getId(),token , grantedAuthorities);
}
#Override
public boolean supports(Class<?> aClass) {
return (JwtAuthenticationToken.class.isAssignableFrom(aClass));
}
}
How do i go about improving on this and end up with a proper login that generates a jwt and keeps it in headers for every request

Spring RESTful Token Web Service Review

I have been going square trying to implement a new RESTful web service using Spring.IO. I've worked through perhaps 30 different online examples that suppose to provide this example but none of them worked 'out of the box'.
A further complication is that 95% of examples use XML configuration exclusively, which in my personal opinion is not as readable as a pure java configuration.
After a great many hours I have managed to cobble together something that 'works' but I would very much be interested in feedback as to my particular implementation. Specifically:
Assuming the client authorisation token is not compromised is my implementation secure.
I was unable to correctly AutoWire the AuthenticationTokenProcessingFilter class in WebSecurityConfig due to the constructor requiring an AuthenticationManager. If there is a way to do this that would help clean things up a little.
The main application class:
#ComponentScan({"webservice"})
#Configuration
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The WebSecurityConfig class (AFAIK this does the majority of the job previously performed by the XML):
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter;
#Autowired
CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
authenticationTokenProcessingFilter =
new AuthenticationTokenProcessingFilter(authenticationManager());
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationTokenProcessingFilter, AnonymousAuthenticationFilter.class)
.httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint);
}
}
The AuthenticationTokenProcessingFilter which performs the token authentication on client connect:
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
AuthenticationManager authManager;
public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
this.authManager = authManager;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
#SuppressWarnings("unchecked")
Map<String, String[]> parms = request.getParameterMap();
if(parms.containsKey("authToken")) {
String token = parms.get("authToken")[0];
// Validate the token
User user = TokenUtils.getUserFromToken(token);
// If we managed to get a user we can finish the authentication
if (user!=null) {
//Add a default authority for all users
List<GrantedAuthority> grantedAuths = new ArrayList();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
// build an Authentication object with the user's info
AbstractAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, token, grantedAuths);
// set the authentication into the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
// continue thru the filter chain
chain.doFilter(request, response);
}
}
The TokenUtils class used by AuthenticationTokenProcessingFilter:
public class TokenUtils {
/**
* Get an authorisation token for the provided userId
*
* #param userId
* #return
*/
public static String getToken(long userId) {
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Attempt to get a user for the provided token
*
* #param token
* #return User if found, otherwise null
*/
public static User getUserFromToken(String token) {
throw new UnsupportedOperationException("Not implemented yet!");
}
}
The CustomAuthenticationEntryPoint, which returns a 403 if the user was not successfully authenticated:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
}
}
An finally my web service entry points:
#RestController
public class EntryPoints {
#RequestMapping(value = "/login", method={RequestMethod.POST})
public LoginResponse login(#RequestParam(value="username", required=true) String username,
#RequestParam(value="password", required=true) String password) {
LoginRequest loginRequest = new LoginRequest(username, password);
//Authenticate the user using the provided credentials
//If succesfull return authentication token
//return new LoginResponse(token);
throw new UnsupportedOperationException("Not implemented yet!");
}
#RequestMapping(value = "/account", method={RequestMethod.POST})
public AccountResponse account(#RequestParam(value="accountId", required=true) long accountId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//Return the request account information
throw new UnsupportedOperationException("Not implemented yet!");
}
}

Categories