Spring Cloud Single Sign On but only if already logged in - spring

I've got a spring cloud oauth2 server up and running with jwt and a client which config looks like this
#EnableOAuth2Sso
public class PortalApplication {
public static void main(String[] args) {
SpringApplication.run(PortalApplication.class, args);
}
#Component
public static class LoginConfigurer extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = new Cookie("XSRF-TOKEN", csrf.getToken());
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
This is all fine and the app redirects to the auth server and redirects back to the Portal app when done.
Client number two
But now I've got another client which is a web shop.
If the user is logged in at the auth server then the web shop should display a bar in the top with links to the users previous orders and a profile.
If the user is NOT logged in, then nothing should happen until the checkout step where they will have to either login or create an account.
Question
How can I auto login if I'm already logged in to the auth server, but allow anonymous users otherwise?
Can I somehow use the spring.oauth2.resource.userInfoUri to achieve this?

Related

Authorize using acess token

I have secured my application using oauth2 standard flow with amazon cognito. It works fine.
Sometimes i want to link to my webapp through my mobile app, and i dont want the user to sign in again. How do i create an #AnonymousAllowed endpoint that takes in an access token, authenticates using spring security and redirects to the homepage of my application?
I've tried creating an endpoint that returns the jsessionid, then another anonymous endpoint that accepts a jsessionid, sets the cookie and redirects to the homepage, but it didnt work.
I've tried setting the authorization header to the access token. But i feel like that wont work properly because the token will expire after few minutes, and spring wont handle authorization for me, rather i have to do it "manually"
I don't know if this can help in your situation. But you can check if anything you can refer from below. It's a custom filter with custom token validator.
You can use Custom TokenAuthenticationFilter by extending AbstractAuthenticationProcessingFilter
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private TokenValidatorService tokenValidatorService;
public CustomTokenAuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
String token= httpServletRequest.getHeader(CommonConstants.OAUTH_HEADER_KEY_AUTHORIZATION);
//Get the custom header
//Validate token using custom validator based on header value
Optional<OAuthResponseData> oauthResponseData = tokenValidatorService.validateAccessToken(token, provider);
...
Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(oauthResponseData.get(), oauthResponseData.get().getOauthToken());
return getAuthenticationManager().authenticate(requestAuthentication);
}
#Override
protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
In TokenValidatorService you can implement OAuth2TokenValidator to validate the token
Spring Security config class:
public class SecurityConfig extends GlobalAuthenticationConfigurerAdapter {
#Configuration
public static class CustomTokenSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
protected CustomTokenAuthenticationProvider customTokenAuthenticationProvider;
#Override
public void configure(final WebSecurity webSecurity) {
webSecurity.ignoring()
...
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.and()
.addFilterBefore(tokenAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS).authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
#Bean
public CustomTokenAuthenticationFilter tokenAuthenticationFilter() throws Exception {
final CustomTokenAuthenticationFilter filter = new CustomTokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
// filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customTokenAuthenticationProvider);
}
}
}

How to configure two security configs with two filters in spring boot correctly?

I've implmemented security in my spring boot microservices project, the requirment is to have
two types of configurations, one for user request (from angular) and one from other services.
The design is to use JWT token for user request and API key for system calls.
Here is the config file (one file) but have also try to split it to two files with no impact:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public static class APISecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${my.api.key.header}")
private String principalRequestHeader;
#Value("${my.api.key.token}")
private String principalRequestValue;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.antMatcher("/api/users/**")
.authorizeRequests() //
.anyRequest().authenticated()
.and()
.addFilterBefore(new APIKeyAuthFilter(principalRequestHeader, principalRequestValue), UsernamePasswordAuthenticationFilter.class);
}
}
#Order(2)
#Configuration
public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/users/**");
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable().csrf().disable();
httpSecurity
.authorizeRequests()
.antMatchers("/users/UserEmailExist", "/users/User/Add", "/users/Authenticate",
"/users/User/ChangePassword")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/users/**").hasAnyRole(ROLE_ADMIN_USER, ROLE_MANAGER_USER)
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
}
Each config has a filter attached to it, here the api one:
public class APIKeyAuthFilter extends GenericFilterBean {
private String principalRequestHeader;
private String principalRequestValue;
public APIKeyAuthFilter(String principalRequestHeader, String principalRequestValue) {
super();
this.principalRequestHeader = principalRequestHeader;
this.principalRequestValue = principalRequestValue;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if(request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
String apiKey = getApiKey((HttpServletRequest) request);
if(apiKey != null) {
if(apiKey.equals(principalRequestValue)) {
ApiKeyAuthenticationToken apiToken = new ApiKeyAuthenticationToken(apiKey, AuthorityUtils.NO_AUTHORITIES);
SecurityContextHolder.getContext().setAuthentication(apiToken);
} else {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(401);
httpResponse.getWriter().write("Invalid API Key");
return;
}
}
}
chain.doFilter(request, response);
}
}
Here is the filter for jwt (normal user from angular):
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private MyUserDetailsService userDetailsService;
#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 username = jwtUtils.getUserNameFromJwtToken(jwt);
MSUserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
}
I've created two different controllers, one with prefix /api/users and second /users.
Here is what happen in two different scenarios:
The user login from Angular, get jwt token and process request which end up in the Jwt filter,
this scenarion looking good with no issues as the user is able to process request as long
he is authenticate.
Microservice send a request with api-key to url with /api/users prefix, it ended up on the same
filter the normal user ended which is not correct and without JWT token he is actually
able to proceed to the controller and process the request without going
to the correct filter.
The only solution I have is to have only one filter and process the header
for api-key and jwt but it doesn't seem right.
I've looked online and try to figure out what I'm doing wrong but no clue as of now.
An update on this issue so I hope it will help to the community.
Firstly, I removed the following code and this mainly fix the problem:
// #Override
// public void configure(WebSecurity web) throws Exception {
// web.ignoring().antMatchers("/api/users/**");
// }
The way the solution work as a whole is that the first configuration #Order(1) you
define .antMatcher which means the configuration will work only for urls that match
the prefix.
So now, scenario 1. User from Angular go the the JWT filter only.
scenario 2. API user will lend in the API filter first! But once it's done (After succesfull authentication) it still
continue to the JWT filter but becuase it doesn't have JWT the filter not doing anything.
I would like to avoid to other filter in case of API call but the solution work,
problem solved.
I must say that security in spring boot is the most complex I came across so far from other features.
Because the AuthTokenFilter is instantiated with #Bean, which causes the filter to be added to the ApplicationFilterChain, after the APIKeyAuthFilter is processed, it can also enter the AuthTokenFilter.

Redirect to original URL after successful authentication in Spring Security

I have the following security configuration class in a Spring Cloud Gateway application. This gateway acts as an OAuth2 client handling the user authentication. After a successful authentication, I'd like to redirect to the URL of the single-page application where the user originally came from.
Example
If the user was on http://localhost:8093/profile then this should be the redirect URL.
Currently I only use a hardcoded value which works for testing purposes. Is there a way to get the "original URL" and use it as a redirection URL?
#Configuration
#EnableWebFluxSecurity
public class SecurityConfiguration {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
httpSecurity
.csrf().disable()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2Login()
// Use original URL here?
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("http://localhost:8093"))
.and()
.exceptionHandling().authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.oauth2ResourceServer().jwt();
return httpSecurity.build();
}
}
You can try below provide the combination to Achieve what you are looking for:
First of all you need to create your Authentication Success Handler:
public class MySimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
protected Log logger = LogFactory.getLog(this.getClass());
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
Then handle Method implementation:
protected void handle(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication
) throws IOException {
//This will provide you last URL
String targetUrl = request.getHeader("referer");
if (response.isCommitted()) {
logger.debug(
"Response has already been committed. Unable to redirect to "
+ targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
Just an FYI:
Note: the HTTP referer is a client-controlled value and can thus be spoofed to something entirely different or even removed. This value should not be used for any critical operation.
Maybe it's too late, but I had the same problem like you. Has Jayesh said, you need to create a class "Authentication Success Handler" to add some logic and redirection after a sucessfull Oauth2 authentication.
But this new class , instead of extending SimpleUrlAuthenticationSucessHandler, must extends SavedRequestAwareAuthenticationSucessHandler and override the method onAuthenticationSucess().
public class OAuth2LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
private UserService userService;
#Autowired
private MessageSource messageSource;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();
User existingUser = userService.findByUsername(oAuth2User.getEmail());
if (existingUser != null) {
// update of user with providerId and authenticationProvider if not already done
log.info(messageSource.getMessage("global.existing-user.oauth2-authenticated",
new Object[] { existingUser }, LocaleContextHolder.getLocale()));
if (existingUser.getAuthenticationProvider() == AuthProvider.LOCAL) {
userService.updateUserFromOAuth2Authentication(oAuth2User, existingUser);
} else if ((!Objects.equals(existingUser.getIdProvider(), oAuth2User.getproviderId())
|| existingUser.getAuthenticationProvider() != oAuth2User.getClientProvider())) {
throw new OAuth2AuthenticationException("a problem occured with Oauth2Authentication!");
}
} else {
// creation of new user
log.info(messageSource.getMessage("global.not-existing-user.oauth2-authenticated",
new Object[] { "createdUser" }, LocaleContextHolder.getLocale()));
userService.saveUserFromOAuth2Authentication(oAuth2User);
}
super.onAuthenticationSuccess(request, response, authentication);
}
}
In your configuration class for security, you just have to call the method successHandler()for Oauth2Login to use your new class "authentication success handler" without of course, using method defaultSucessUrl() , like this
http.oauth2Login()
.loginPage("/app/login")
.userInfoEndpoint()
.userService(oauth2UserService)
.and()
.successHandler(oAuth2LoginSuccessHandler)
;
Sorry for my bad english, i found this solution just after reading this article https://www.baeldung.com/spring-security-redirect-login

SpringBoot different auths (MS AD & JWT) to one Controller

I tried to implement small API Gateway for my Mobile App on Spring Boot.
In my architecture i uses MS Active Directory Server for auth staff of company and in future will sms verify code for clients company for sending JWT.
I'm not use layer DAO, UsersRepository and DB connect.
All HTTP requests sending via RestTemplate from Services layer to our inthernal CRM-system.
I implements LDAP AD auth is very simple HttpBasic configuration bellow:
#Configuration
#EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf()
.disable()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/v1/send/**").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new
ActiveDirectoryLdapAuthenticationProvider("mydomain.com", "ldap://192.168.0.100:389/");
activeDirectoryLdapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
activeDirectoryLdapAuthenticationProvider.setUseAuthenticationRequestCredentials(true);
activeDirectoryLdapAuthenticationProvider.setSearchFilter("(&(objectClass=user)(userPrincipalName={0})(memberOf=CN=mobileaccess,OU=User Groups,OU=DomainAccountsUsers,DC=MYDOMAIN,DC=COM))");
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider);
auth.eraseCredentials(true);
}
}
I have two RestController V1 and V2 for example:
#RequestMapping("api/v1")
//get token for staff (AD user) HttpBasic auth
#PostMapping("auth/get/stafftoken")
public ResponseEntity<?> getToken() {
// some code...
HttpHeaders tokenHeaders = new HttpHeaders();
tokenHeaders.setBearerAuth(tokenAuthenticationService.getToken());
return new ResponseEntity<>(tokenHeaders, HttpStatus.OK);
}
//get JWT if code from sms == code in my CRM-system (for client) not auth - permitAll
#PostMapping("send/clienttoken")
public #ResponseStatus
ResponseEntity<?> sendVerifyCode(#RequestParam("verifycode") String verifycode) {
// some code...
HttpHeaders tokenHeaders = new HttpHeaders();
tokenHeaders.setBearerAuth(tokenAuthenticationService.getToken());
return new ResponseEntity<>(tokenHeaders, HttpStatus.OK);
}
#RequestMapping("api/v2")
#GetMapping("get/contract/{number:[0-9]{6}")
public Contract getContract(#PathVariable String number) {
return contractsService.getContract(number);
}
How to implements Bearer Auth requests to Controller APIv2 with JWT tokens (clients and staff)?
I think this is implemented through filter chain?
So guys
If you implements multi authentification as in my example, first of all create utility class for builds token and validation users JWT. This is standard code, for example:
public static String createUserToken(Authentication authentication) {
return Jwts.builder()
.setSubject(authentication.getName())
.claim(authentication.getAuthorities())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SIGN_KEY)
.compact();
}
public static Authentication getAuthentication(HttpServletRequest request) {
String token = extractJwt(request);
try {
if (token != null) {
Claims claims = Jwts.parser().setSigningKey(SIGN_KEY).parseClaimsJws(token).getBody();
String username = Jwts.parser().setSigningKey(SIGN_KEY).parseClaimsJws(token).getBody().getSubject();
return username != null ? new UsernamePasswordAuthenticationToken(username, "", Collections.EMPTY_LIST) : null;
}
} catch (ExpiredJwtException e) {
}
return null;
}
Аfter you should create two filters:
LoginAuthentificationFilter extends BasicAuthenticationFilter
JwtAuthenticationFilter extends GenericFilterBean
Code example below
public class LoginAuthentificationFilter extends BasicAuthenticationFilter {
public LoginAuthentificationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
super.doFilterInternal(request, response, chain);
}
}
public class JwtAuthenticationFilter extends GenericFilterBean {
private RequestMatcher requestMatcher;
public JwtAuthenticationFilter(String path) {
this.requestMatcher = new AntPathRequestMatcher(path);
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (!requiresAuthentication((HttpServletRequest) servletRequest)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
Authentication authentication = JwtUtils.getAuthentication((HttpServletRequest) servletRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(servletRequest, servletResponse);
}
private boolean requiresAuthentication(HttpServletRequest request) {
return requestMatcher.matches(request);
}
}
And at the end
Settings WebSecurityConfigurerAdapter
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/v1/noauth_endpoints").permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterAt(jwtFilter(), BasicAuthenticationFilter.class)
.addFilter(loginFilter());
http.headers().cacheControl();
}
Beans
#Bean
public LoginAuthentificationFilter loginFilter() {
return new LoginAuthentificationFilter(authenticationManager());
}
#Bean
public JwtAuthenticationFilter jwtFilter() {
return new JwtAuthenticationFilter("/api/v2/**");
}
#Bean
#Override
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("bzaimy.com", "ldap://192.168.0.100:389/");
provider.setSearchFilter("(&(objectClass=user)(userPrincipalName={0})(memberOf=CN=mobileaccess,OU=User Groups,OU=DomainAccountsUsers,DC=MYDOMAIN,DC=COM))");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}

Spring Security multiple configuration

I try to make spring boot multiple configuration. There are next configurations. First filter, I use for verify client. It should be in every request. I wanna try make extra rule for verify user permission. It's mean, when someone wants to save something, he/she have to be authorized user and send personal token in header. If that token is valid, I can allow to save file. But JWT token must to be too. Finally, for save I would like to use two tokens. First is JWT and second is user token.
This part of code verify permission for access to API. It should be in header in each request. Now, it works.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Order(1)
#Configuration
public static class JwtTokenSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterAfter(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/new").hasAuthority("ADMIN")
.anyRequest().authenticated();
}
}
This part of code have to verify user. logged in or no. It doesn't work now. When I try to get access to "/v1/save_file", it check only JWT token, not user-token. Finally, I would like to make to checks for that endpoint. first is verify JWT token, second is verify user-token for save.
#Order(2)
#Configuration
public static class UserTokenSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Value("${fc.security.header.user-token:User-Token}")
private String usrTokenHeaderName;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
UserTokenSecurityConfig userToken = new UserTokenSecurityConfig(usrTokenHeaderName);
userToken.setAuthenticationManager(new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!usrTokenHeaderName.equals(principal)) {
throw new BadCredentialsException("The Application token was not found or not the expected value.");
}
System.out.println(principal + " " + usrTokenHeaderName);
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.antMatcher("/v1/save_file")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(userToken)
.addFilterBefore(new ExceptionTranslationFilter(new Http403ForbiddenEntryPoint()), userToken.getClass())
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
}
JWTAuthorizationFilter class
public class JWTAuthorizationFilter extends OncePerRequestFilter {
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
if (checkJWTToken(request, response)) {
Claims claims = validateToken(request);
if (claims.get("authorities") != null) {
setUpSpringAuthentication(claims);
} else {
SecurityContextHolder.clearContext();
}
} else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
return;
}
}
private Claims validateToken(HttpServletRequest request) {
String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
return Jwts.parser().setSigningKey(Constants.SECRET_KEY.getBytes()).parseClaimsJws(jwtToken).getBody();
}
/**
* Authentication method in Spring flow
*
* #param claims
*/
private void setUpSpringAuthentication(Claims claims) {
#SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(claims.getSubject(), null,
authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
SecurityContextHolder.getContext().setAuthentication(auth);
}
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res) {
String authenticationHeader = request.getHeader(HEADER);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX))
return false;
return true;
}
}
very simple UserTokenSecurityConfig class
public class UserTokenSecurityConfig extends AbstractPreAuthenticatedProcessingFilter {
private String userHeader;
public UserTokenSecurityConfig(String userHeader) {
this.userHeader = userHeader;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpServletRequest) {
return httpServletRequest.getHeader(userHeader);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) {
return "NA";
}
}

Resources