Mock spring security roles through the header with JWT - spring

Is possible to make a very simple JWT example with Spring Security.
I want to illustrate how roles can be used, without setting up the all of the spring security.
Basically I want to use postman with a jwt token which contain the roles. I don't want to setup user login. Is possible?

You can create JWTFilter class and extends GenericFilterBean then
override to doFilter.You can resolve jwt token in this method and jwt
is valid then you can get claim
List roles =
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(Jwt).getClaims("roles");
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt)) {
if (this.tokenProvider.validateToken(jwt)) {
}
}
filterChain.doFilter(servletRequest, servletResponse);
}catch(Exception ex){
handleException((HttpServletResponse) servletResponse,ex);
}
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(JWTConfigurer.AUTHENTICATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
String jwt = bearerToken.substring(7, bearerToken.length());
return jwt;
}
String jwt = request.getParameter(JWTConfigurer.AUTHENTICATION_TOKEN);
if (StringUtils.hasText(jwt)) {
return jwt;
}
return null;
}

Related

How to allow swagger ui in Spring Custom Filter along with validation

I have written the following code where I have created a Custom Filter in SpringBoot which is always passed as Request Header. Request header name licenseKey and some value. I have implemented and also allowed Swagger-UI to work. Please suggest me to follow better approach, my seniors say that it is not a good approach. I provide below the code. The task is to receive licenseKey while calling Rest end point and also we need to allow Swagger-UI without licenseKey that will be provided later as part of authorization in Swagger. Currently the code is working fine. I request for better approach. I provide below the code. I removed all import statements.
#Component
public class CustomURLFilter implements Filter {
#Autowired
private CustomUserDetailsService userDetailsService;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomURLFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("########## Initiating CustomURLFilter filter ##########");
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String path = request.getRequestURI();
System.out.println("Path: "+path);
if(path.startsWith("/app-name-service/swagger-ui") || path.startsWith("/app-name-service/v3/api-docs")) {
filterChain.doFilter(request, response);
return;
} else {
String licenseKey = userDetailsService.getLicenseKey(request);
System.out.println("User License Key: "+licenseKey);
}
LOGGER.info("This Filter is only called when request is mapped for /customer resource");
//call next filter in the filter chain
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}

Springboot + Axios JWT HTTPonly cookie is null

I cant get this cookie verification filter to work in springboot. I can also see the cookie in postman but not browser.
I generate the cookie in the following:
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#RequestBody User loginRequest) {
Authentication authentication =
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword())); // gets error here
SecurityContextHolder.getContext().setAuthentication(authentication);
MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();
ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(userDetails);
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority()).collect(Collectors.toList());
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
.body(userService.findUserProfileUserByEmail(userDetails.getEmail()));
}
When a request is sent to a restricted access endpoint, it will be run through this filter in the SecurityConfiguration
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
This is the class:
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private MyUserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String email = jwtUtils.getEmailFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
System.out.println(SecurityContextHolder.getContext());
SecurityContextHolder.getContext().setAuthentication(authentication); // throws error here
System.out.println("a");
}
}
catch (Exception e) { logger.error("Cannot set user authentication: {}", e);
System.out.println(e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) { return jwtUtils.getJwtFromCookies(request); }
}
Here on line String jwt = parseJwt(request), it will always equal null.
I was told this may be an issue with the actual request itself, that it should contain {withCredentials: true} in Axios, though doing this raises other issues, and does not explain why this cookie exists and is visible in Postman.

How to do basic authentication using cookies in spring security?

I am securing my REST api using Basic-Auth. On correct credentials passed by user, a controller is responsible for sending a httpOnly and secure cookie in response.
#GetMapping
#ResponseStatus(value=HttpStatus.OK)
public void loginUser( final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {
setAuthCookieToResonse(request,response);
}
private void setAuthCookieToResonse(final HttpServletRequest request ,final HttpServletResponse response) throws UnsupportedEncodingException {
String cookieKey = "auth";
String cookieValue = request.getHeader("Authorization");
if (cookieValue != null) {
Cookie cookie = new Cookie(cookieKey, cookieValue);
cookie.setHttpOnly(true);
cookie.setSecure(true);
response.addCookie(cookie);
}
}
So, now with each request a cookie is being sent by the browser, which will contain Basic-Auth details. But the problem is, how do the spring security extract those credentials from that cookie?
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {// #formatter:off
httpSecurity
.cors()
.and().authorizeRequests()
.antMatchers("/signup/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
;
}
My guess was:
To add a filter before BasicAuthenticationFilter.class and extract the credentials from cookie and than add those credentials to the HttpServletRequest's Authorizaton header which is going to be passed to spring-security layer. But the problem is, HttpServletRequest doesn't have API to add headers.
What would be the right way to implement this?
I made this working after following this blog (archived). But I would love to hear other solutions, especially using some spring configuration itself. Spring is a very matured framework, it must(should) have something to handle this common problem.
Since, the HttpServletRequest don't have any method to add the new headers, I need to create a custom class which can add new headers to the request, this can be achived by HttpServletRequestWrapper. Here is the implementation.
public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
// holds custom header and value mapping
private final Map<String, String> customHeaders;
public MutableHttpServletRequest(HttpServletRequest request) {
super(request);
this.customHeaders = new HashMap<String, String>();
}
public void putHeader(String name, String value) {
this.customHeaders.put(name, value);
}
public String getHeader(String name) {
// check the custom headers first
String headerValue = customHeaders.get(name);
if (headerValue != null) {
return headerValue;
}
// else return from into the original wrapped object
return ((HttpServletRequest) getRequest()).getHeader(name);
}
public Enumeration<String> getHeaderNames() {
// create a set of the custom header names
Set<String> set = new HashSet<String>(customHeaders.keySet());
// now add the headers from the wrapped request object
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
// add the names of the request headers into the list
String n = e.nextElement();
set.add(n);
}
// create an enumeration from the set and return
return Collections.enumeration(set);
}
}
The filter which checks for the cookie, before the Spring-secuirty:
public class CheckAuthCookieFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(getClass());
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(httpServletRequest);
Cookie[] cookies = httpServletRequest.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
logger.debug(cookie.getName() + " : " + cookie.getValue());
if (cookie.getName().equalsIgnoreCase("auth")) {
mutableRequest.putHeader("Authorization", URLDecoder.decode(cookie.getValue(), "utf-8"));
}
}
}
chain.doFilter(mutableRequest, response);
}
}
and finally the security configuration:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {// #formatter:off
httpSecurity
.cors()
.and().authorizeRequests()
.antMatchers("/signup/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
;
httpSecurity.addFilterBefore(new CheckAuthCookieFilter(), BasicAuthenticationFilter.class);
}
My custom filter will run before the Spring's BasicAuthenticationFilter.If there is a cookie present with name auth(which the application created on successful login), than that's the cookie which holds the basic auth credentials. The credentials are extracted from that, and added to the header of request. Then the BasicAuthenticationFilter will run and look for the Authorization and proceed with its normal flow.

Spring Boot JWT filter logic

I inherited a half-written Spring Boot REST service that is using Spring Sec to implement JWT-based API authentication. Gradle security-related dependencies are:
'org.springframework.security:spring-security-jwt:1.0.9.RELEASE'
'org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE'
'io.jsonwebtoken:jjwt:0.9.0'
'org.springframework.boot:spring-boot-starter-security'
This app uses Spring Sec filters to implement the entire auth solution, and I'm trying to wrap my head around how it works, and for the life of me can't make sense of a few critical things :-/
Here's the code:
public class MyAppAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public MyAppAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
public class MyAppAuthorizationFilter extends BasicAuthenticationFilter {
public MyAppAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET.getBytes())
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private AccountDAO accountDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountDAO.findByUsername(username);
if(account == null) {
throw new UsernameNotFoundException(username);
}
return new User(account.username, account.password, []);
}
}
What I'm not understanding is:
Can I assume that Spring Security automagically positions these filters in the correct sequence? That is: the MyAppAuthenticationFilter always gets called before the MyAppAuthorizationFilter?
I'm really confused by the authenticationManager.authenticate(...) call inside MyAppAuthenticationFilter#attemptAuthentication. How are creds.getUsename() and cred.getPassword() compared to user information stored in a database (or LDAP or anywhere else)? How does this mechanism relate to UserDetailsServiceImpl#loadByUsername(String)?
All of the logic in MyAppAuthorizationFilter#doFilterInternal doesn't make sense to me. To me, I read it as: check to see if there is a JWT token header on the request. If there isn't, then go ahead and make the request any way (!!!!). If there is, then go ahead and check that the JWT has a valid user as its subject. Shouldn't we be blocking the request if there's no JWT header on the request?

How to secure spring boot data rest security based on logged in user?

I have a spring data rest micro-service, with following entities
User
Laptop
Specs
User has list of laptops, and each one has it's own specs. Now the point is, user can log in through another micro-service, and get a JWT. Then, he creates a laptop for himself, and adds tech specs to the laptop.
Here's the call for adding the specs to the laptop
curl -i -X PUT http://localhost:8080/laptop/1/specs -H "Content-Type:text/uri-list" -d "http://localhost:8080/specs/1"
I can get my user's id from the JWT in my security filter, so how can I validate that the resource "belongs" to the user? How do I make sure that the user doesn't update other user's laptop specs? This is just the showcase, in fact number of models is around 30, so is there a dynamic solution to verify the user/resource association?
Here's the code of my security filter
public class JWTAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = JWTAuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
JWT validator
public static Authentication getAuthentication(HttpServletRequest request) {
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
String authToken = authHeader != null ? authHeader.replace(TOKEN_PREFIX, "") : null;
if (authToken != null) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(authToken)
.getBody();
String username = claims.getSubject();
#SuppressWarnings("unchecked") final List<String> authoritiesClaim = (List<String>) claims.get(AUTHORITIES);
final List<SimpleGrantedAuthority> authorities = authoritiesClaim
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return username != null ?
new UsernamePasswordAuthenticationToken(username, null, authorities) :
null;
}
return null;
}
and web security configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}

Resources