[[Edited]] JWT spring security: How to have roles be read by .hasRole() or .hasAuthority(), where they look at the user roles - spring

I am trying to add an authorization to the spring actuator service using roles in the HTTP config but it doesn't work and the response is "forbidden 403" which means the user is unauthorized.
So My question is where exactly the .hasRole() finds the signed in user roles when using JWT token
Here is the config method in the class which extends WebSecurityConfigurerAdapter class
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new JwtTokenVerifier(jwtConfig), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/actuator/**").hasRole("ACTUATOR")
.anyRequest()
.authenticated()
.and()
.formLogin().disable();
}
Here I put the roles in the JWT Token
public String generateJwtToken(UserDetailsImpl userDetails) {
Map<String, Object> claims = new HashMap<>();
Set<String> Userroles = new HashSet<>();
Role r1 = new Role();
r1.setDescription("ROLE_ACTUATOR");
r1.setId(1L);
Set<Role> roles = new HashSet<>();
roles.add(r1);
for(Role role:roles){
Userroles.add(role.getDescription());
}
claims.put("Roles",Userroles.toArray());
claims.put("userId", userDetails.getId());
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(java.sql.Date.valueOf(LocalDate.now().plusDays(getTokenExpirationAfterDays())))
.signWith(Keys.hmacShaKeyFor(getSecretKey().getBytes())).compact();
}
So what is wrong or missing? Thanks in advance
Here is an update of the JWT custom filter:
public class JwtTokenVerifier extends OncePerRequestFilter {
private JwtConfig jwtConfig;
public JwtTokenVerifier(JwtConfig jwtConfig) {
super();
this.jwtConfig = jwtConfig;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (authorizationHeader == null || authorizationHeader.isEmpty() || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
String requestParam = request.getParameter("token");
if (requestParam != null && !requestParam.isEmpty() && requestParam.startsWith(jwtConfig.getTokenPrefix())) {
authorizationHeader = requestParam;
} else {
filterChain.doFilter(request, response);
return;
}
}
try {
if (jwtConfig.validateJwtToken(authorizationHeader)) {
String username = jwtConfig.getUserNameFromJwtToken(authorizationHeader);
Authentication auth = new UsernamePasswordAuthenticationToken(username, new WebAuthenticationDetailsSource().buildDetails(request), null);
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (Exception e) {
e.printStackTrace();
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
}
filterChain.doFilter(request, response);
}
}

In the WebSecurityConfigurerAdapter extention, I see ACTUATOR, but in the user details definition I see ROLE_ACTUATOR. Is this a mismatch?

Thanks to #SteveRiesenberg, as said in the chat "if roles are not handled by your custom JWT filter, then that is the issue. Roles (authorities) must be populated when the authentication is performed."
so since the roles are not handled in the filter, they are not passed through the security chain.
The code of the filter is edited as follows:
UserDetailsImpl userDetails = (UserDetailsImpl) userDetailsService.loadUserByUsername(username);
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
Authentication auth = new UsernamePasswordAuthenticationToken(username, new WebAuthenticationDetailsSource().buildDetails(request), authorities);
Where the authorities are passed to the UsernamePasswordAuthenticationToken and UserDetailsImpl is the implementation of UserDetails class provided by spring

Related

Spring Security Redirecting After Successful Authentication

I am trying to add access control to a set of api endpoints and the problem I am running into is that the service is redirecting to / regardless of whether the original request was /api/apple or /api/orange. I currently have a filter set up to read a custom http header to do the authentication and the filter I am using is extended from AbstractAuthenticationProcessingFilter. The documentation is saying that it is intended for the AbstractAuthenticationProcessingFilter to redirect to a specific url upon successful authentication, but this is not the behavior I want for an api. I think I may be using the wrong Filter, but I don't know which one I should be using. Can I get some help on what I may be doing wrong and what I should be doing?
Filter Chain Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
AuthenticationManager customAuthenticationManager(PreAuthProvider preAuthProvider) {
return new ProviderManager(List.of(preAuthProvider));
}
#Bean
SessionAuthFilter customAuthFilter(AuthenticationManager authManager, CustomUserDetails userDetails) {
return new SessionAuthFilter(
new OrRequestMatcher(
new AntPathRequestMatcher("/apple/**"),
new AntPathRequestMatcher("/orange/**")
),
authManager,
userDetails);
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, SessionAuthFilter authFilter) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.accessDeniedHandler(new AccessDeniedHandlerImpl())
.and()
.formLogin().disable()
.httpBasic().disable()
.authorizeRequests()
.antMatchers(
"/",
"/error",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui.html",
"/actuator/**"
).permitAll()
.antMatchers(GET, "/apple").hasAuthority("getApples")
.antMatchers(GET, "/orange").hasAuthority("getOranges")
.anyRequest().authenticated()
.and()
.addFilterBefore(authFilter, AbstractPreAuthenticatedProcessingFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
Filter Implementation:
public class SessionAuthFilter extends AbstractAuthenticationProcessingFilter {
private final CustomUserDetails userDetails;
protected SessionAuthFilter(RequestMatcher requestMatcher, AuthenticationManager authenticationManager,
CustomUserDetails userDetails) {
super(requestMatcher, authenticationManager);
this.userDetails = userDetails;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
var sessionToken = request.getHeader("SessionToken") != null ? request.getHeader("SessionToken").trim() : null;
var user = userDetails.loadUserByUsername(sessionToken);
var authentication = new PreAuthenticatedAuthenticationToken(user.getUsername(), user.getPassword(),
user.getAuthorities());
authentication.setAuthenticated(user.isCredentialsNonExpired());
authentication.setDetails(userDetails);
SecurityContextHolder.getContext().setAuthentication(authentication);
return this.getAuthenticationManager().authenticate(authentication);
}
}
Authentication Provider:
#Component
#Slf4j
public class PreAuthProvider implements AuthenticationProvider {
private boolean throwExceptionWhenTokenRejected;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!this.supports(authentication.getClass())) {
return null;
} else {
log.debug(String.valueOf(LogMessage.format("PreAuthenticated authentication request: %s", authentication)));
if (authentication.getPrincipal() == null) {
log.debug("No pre-authenticated principal found in request.");
if (this.throwExceptionWhenTokenRejected) {
throw new BadCredentialsException("No pre-authenticated principal found in request.");
} else {
return null;
}
} else if (authentication.getCredentials() == null) {
log.debug("No pre-authenticated credentials found in request.");
if (this.throwExceptionWhenTokenRejected) {
throw new BadCredentialsException("No pre-authenticated credentials found in request.");
} else {
return null;
}
} else if (!authentication.isAuthenticated()) {
throw new InsufficientAuthenticationException("Session token likely no longer valid.");
}
return authentication;
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(PreAuthenticatedAuthenticationToken.class);
}
public void setThrowExceptionWhenTokenRejected(boolean throwExceptionWhenTokenRejected) {
this.throwExceptionWhenTokenRejected = throwExceptionWhenTokenRejected;
}
}
It looks like if you set continueChainBeforeSuccessfulAuthentication in your AbstractAuthenticationProcessingFilter implementation to true, you can delay the redirection. Using your own success handler implementation will completely stop the redirect behavior. I only needed to modify the filter constructor which came out to be:
protected SessionAuthFilter(RequestMatcher requestMatcher, AuthenticationManager authenticationManager,
CustomUserDetails userDetails) {
super(requestMatcher, authenticationManager);
this.userDetails = userDetails;
this.setContinueChainBeforeSuccessfulAuthentication(true);
this.setAuthenticationSuccessHandler((request, response, authentication) -> {});
}
The other approach would be to implement a different Filter such as OncePerRequestFilter or a GenericFilterBean to handle the authentication yourself.

Json authentication in spring boot with flutter front end

I am trying to make a log in page in spring boot with a flutter front end by sending the username and password through JSON. The IP sends the request from my android emulator to localhost.
The following flutter code runs when a submit button is pressed:
Future<String> sendLogin(
String username, String password, BuildContext context) async {
var url = "http://10.0.2.2:8080/login";
var response = await http.post(Uri.parse(url),
headers: <String, String>{"Content-Type": "application/json"},
body: jsonEncode(<String, String>{
"username": username,
"password": password,
}));
url = "http://10.0.2.2:8080/protected-resource";
response = await http.get(Uri.parse(url));
return response.body;
}
My spring boot back-end looks like this
-Security configuration:
#Configuration
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private MyAuthenticationProvider authProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomFilter mupaf = new CustomFilter();
mupaf.setAuthenticationManager(authenticationManager());
http
.csrf().disable()
.addFilterAt(
mupaf,
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/h2/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/protected-resource").authenticated()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
http.authenticationProvider(authProvider);
}
}
The custom filter:
public class CustomFilter extends AbstractAuthenticationProcessingFilter {
protected CustomFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
#Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username, password;
try {
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = requestMap.get("username");
password = requestMap.get("password");
System.out.println(username+ " "+password+" Tried to log in");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
Authentication provider:
#Component
#RequiredArgsConstructor
public class MyAuthenticationProvider implements AuthenticationProvider {
private final UserRepository userRepository;
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
final UsernamePasswordAuthenticationToken upAuth = (UsernamePasswordAuthenticationToken) authentication;
final String name = (String) authentication.getPrincipal();
final String password = (String) upAuth.getCredentials();
final String storedPassword = userRepository.findByUsername(name).map(User::getPassword)
.orElseThrow(() -> new BadCredentialsException("illegal id or passowrd"));
if (Objects.equals(password, "") || !Objects.equals(password, storedPassword)) {
throw new BadCredentialsException("illegal id or passowrd");
}
final Object principal = authentication.getPrincipal();
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
Collections.emptyList());
result.setDetails(authentication.getDetails());
return result;
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
}
When I send a username and password through postman I am able to log in and access protected resources. But when I do the same through the flutter front end, the log in is successful but the protected resource can't be accessed (Error 403).
What am I missing here that causes the issue?

spring security - jwt request filter validates and authenticate session but principal is null

After updating my spring boot from 2.0.3 to 2.2.1 somehow my Spring Security configuration stop working.
setup is as follow - I would like to have all my request processed by request filter where JWT token is validated and UserDetails are created. So config is pretty easy:
#Configuration
#EnableWebSecurity
public class NoAuthConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
}
}
and filter class
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class JwtRequestFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JwtRequestFilter.class);
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtUtils jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String requestTokenHeader = "";
try{
requestTokenHeader = WebUtils.getCookie(request, "token").getValue();
} catch (NullPointerException ex ){}
if (requestTokenHeader != null && requestTokenHeader.contains(".")) {
jwtToken = requestTokenHeader;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
log.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
log.error("JWT Token has expired");
}
}
if (username != null ) {
UserDetails userDetails = null;
userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
} else {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
chain.doFilter(request, response);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String[] AUTH_WHITELIST = {
"/v2/auth/",
"/api/packetdiscovery"
};
String path = request.getServletPath();
return (StringUtils.startsWithAny(path, AUTH_WHITELIST));
}
}
and by debugging I can see that everything work smoothly until request reach Controller where NullPointerException Is thrown on Principal
#PreAuthorize("hasAuthority('ROLE_USER')")
#GetMapping(value = "/userinfo")
public ResponseEntity<SessionOwner> getSesstionOwner(Principal user) {
return dashboardService.getSessionOwner(user.getName());
}
Could anyone give me advice on how to handle it?

Unable to implement Role based access to Spring-Boot API

I am new to Spring-Boot.I want to create an API which will have role based access with JWT token based authentication. But, unable to implement that.
I am not using JPA & Hibernate to fetch and map data. Instead I am using Ibatis.I have tried with #PreAuthorize and antMatchers & hasRole, but failed. By getting user id from JWT token, I am fetching details and roles and setting those to SecurityContextHolder.getContext().setAuthentication, still not working.
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.authorizeRequests()
.anyRequest().authenticated()
.antMatchers("api/management/reports").hasRole("Supervisor");
}
Controller
#RestController
#RequestMapping("api")
#CrossOrigin
public class MyController {
#PreAuthorize("hasRole('Supervisor')")
#GetMapping("username")
public String reports(){
SecurityContext securityContext = SecurityContextHolder.getContext();
return securityContext.getAuthentication().getName();
}
}
AuthorizationFilter
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(JwtProperties.HEADER_STRING);
if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
Authentication authentication = getUsernamePasswordAuthentication(request,header);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request, String header) {
try {
String token = header.replace(JwtProperties.TOKEN_PREFIX,"");
String userName = JWT.require(HMAC512(JwtProperties.SECRET.getBytes()))
.build()
.verify(token)
.getSubject();
List<User> searchedUserList = getUserDetailsDAO().getUserDetails(userName);
if (null !=searchedUserList && searchedUserList.size()>0) {
User searchedUser = new User();
searchedUser = searchedUserList.get(0);
List<RoleAccess> roleAccessList = new ArrayList<RoleAccess>();
XrefUsrRole oXrefUsrRole = new XrefUsrRole();
oXrefUsrRole.setUserName(searchedUser.getUsername());
roleAccessList = getRoleAccessDAO().getAccessDetails(oXrefUsrRole);
List<GrantedAuthority> authorities = uildUserAuthority(roleAccessList);
org.springframework.security.core.userdetails.User newUser = buildUserForAuthentication(searchedUser, authorities);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(newUser, null,authorities);
return auth;
}
return null;
}
return null;
} catch (IOException e) {
return null;
}
private org.springframework.security.core.userdetails.User buildUserForAuthentication(User searchedUser, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(searchedUser.getUsername(), searchedUser.getPassword(), true, true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(List<RoleAccess> roleAccessList) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (RoleAccess userRole : roleAccessList) {
setAuths.add(new SimpleGrantedAuthority("ROLE_"+userRole.getModifiedBy()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
In this case api/username should not accessible except users having Supervisor role.
You have ROLE_"+userRole.getModifiedBy()) which means you are granting roles with ROLE_NAME and in PreAuthorize you have Supervisor which is causing the issue. You can store role as ROLE_SUPERVISOR in a database then use it as below
// Build user's authorities
for (RoleAccess userRole : roleAccessList) {
setAuths.add(new SimpleGrantedAuthority("ROLE_"+userRole.getModifiedBy()));
}
use
#PreAuthorize("hasRole('ROLE_SUPERVISOR')")
.antMatchers("api/management/reports").hasRole("SUPERVISOR");

Spring Boot and Spring Security filter not filtering correct request

I have a spring boot and spring security service.
I have extended WebSecurityConfigurerAdapter class and overridden configure method. But somehow it is not filtering correct request.
My url is something like -
localhost:8080/album/private/v1/getAlbumsByVendorId?vendorId=1
localhost:8080/vendor/private/v1/getVendor?vendorId=1
and also I have some URL which I do not want to authenticate.like below url.
localhost:8080/category/v1/getCategory
Only want to authenticate if the URL contains private.
But seems like my filter is getting invoked for all request.
is there something wrong in .antMatchers("/**/private/**")
Note - I don't have any context path as of now.
Added the classes.
Controller is just a dummy test controller.
#Configuration
#EnableWebSecurity
//#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers("/**/private/**").authenticated()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
}
}
#Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
//Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
#RestController
#CrossOrigin()
public class HelloWorldController {
#RequestMapping({ "/hello" })
public String hello() {
return "Hello World";
}
#RequestMapping({ "/private/test" })
public String hello2() {
return "Hello World-test";
}
#RequestMapping({ "/v1/private/test" })
public String hello3() {
return "Hello World-test-v1";
}
#RequestMapping({ "/v1/public/test" })
public String hello4() {
return "Hello World-test-v1-public";
}
}
By default, Spring Boot will secure all endpoints when Spring Security is on the classpath. We need to explicitly add an exclusion for all other endpoints to be permitted without authentication. Consider change is .anyRequest().permitAll(),
which means each request other than /**/private/** will be accessible to everyone. In other words, the filter will only apply to /**/private/**
Git Link
approach 1 (clean way)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers("/**/private/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
}
approach 2: only check for a token if Request comes from /private/ (not an ideal way)
JwtAuthenticationEntryPoint.java
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
System.out.println("Entry Request: "+request.getRequestURI());
System.out.println("Entry Contain: "+request.getRequestURI().contains("private"));
if(request.getRequestURI().contains("private")==true)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
JwtRequestFilter.java
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
System.out.println("JWT Request: "+request.getRequestURI());
System.out.println("JWT Contain: "+request.getRequestURI().contains("private"));
String username = null;
String jwtToken = null;
//Remove comment for second approach
if(request.getRequestURI().contains("private")==false)
{
System.out.println("Do Noting, Permit It");
chain.doFilter(request, response);
}
else if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ") ) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
http://localhost:8080/v1/private/test **401**
http://localhost:8080/v1/public/test **200**

Resources