We have implemented Spring Security in our angular spring boot project.
Here we are getting exception spring security User account is locked
Please review the following code.
SecurityConfiguration.java
package com.jwt.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import com.jwt.security.filter.AuthenticationTokenFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration<jwtAuthenticationEntryPoint> extends WebSecurityConfigurerAdapter{
#Autowired private UserDetailsService userDetailsService;
#Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint ;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder( PasswordEncoder());
}
#Bean
public PasswordEncoder PasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationTokenFilter authenticationTokenFilterBean( ) {
return new AuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/login").permitAll()
.antMatchers(HttpMethod.OPTIONS ,"/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
httpSecurity.headers().cacheControl();
httpSecurity.headers().httpStrictTransportSecurity().includeSubDomains(true).maxAgeInSeconds(31536000);
}
}
the authentication token filter AuthenticationTokenFilter.hjava
package com.jwt.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import com.jwt.security.JwtTokenUtil;
public class AuthenticationTokenFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.header}")
private String tokenHeader;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
String authToken = request.getHeader(this.tokenHeader);
if (authToken != null && authToken.length() > 7) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isValid = jwtTokenUtil.validateToken(authToken, userDetails);
if (isValid) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
Here i am getting null for authToken when running from postman
The code for JwtUtil is as following
package com.jwt.security;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
#Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
#Value("${jwt.secret}")
private String secret;
#Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String authToken) {
String username = null;
try {
final Claims claims = getClaimsFromToken(authToken);
username = claims.getSubject();
} catch (Exception e) {
// TODO Auto-generated catch block
username = null;
}
return username;
}
private Claims getClaimsFromToken(String authToken) {
// TODO Auto-generated method stub
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken).getBody();
} catch (Exception e) {
// TODO Auto-generated catch block
claims = null;
}
return claims;
}
public boolean validateToken(String authToken, UserDetails userDetails) {
// TODO Auto-generated method stub
JwtUser user = (JwtUser) userDetails;
final String username = getUsernameFromToken(authToken);
return (username.equals(user.getUsername()) && !isTokenExpired(authToken));
}
private boolean isTokenExpired(String authToken) {
final Date expiration = getExpirationDateFromToken(authToken);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String authToken) {
// TODO Auto-generated method stub
Date expiration = null;
final Claims claims = getClaimsFromToken(authToken);
if (claims != null) {
expiration = claims.getExpiration();
} else {
expiration = null;
}
return expiration;
}
public String generateToken(JwtUser userDetails) {
Map<String,Object> claims = new HashMap<String,Object>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
public String generateToken(Map<String , Object> claims ) {
return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS512, secret).compact();
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
}
The code for CsrfHeaderFilter is as following
package com.jwt.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
public class CsrfHeaderFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("...CsrfToken.class.getName() :::" + CsrfToken.class.getName());
// CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
// CsrfToken csrfToken = new HttpSessionCsrfTokenRepository().loadToken(request);
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
String token = null;
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
if(csrfToken != null) {
token = csrfToken.getToken();
}
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
filterChain.doFilter(request, response);
}
}
The controller used is AuthenticationController The code is as following
package com.jwt.security.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.jwt.security.JwtTokenUtil;
import com.jwt.security.JwtUser;
import com.jwt.security.domain.User;
import com.jwt.security.domain.UserDTO;
import com.jwt.security.exception.UnauthorizedException;
#RestController
public class AuthenticationController {
#Value("${jwt.header}")
private String tokenHeader;
#Autowired private AuthenticationManager authenticationManager;
#Autowired private JwtTokenUtil jwtTokenUtil;
#PostMapping(value="/login")
public ResponseEntity<UserDTO> login(#RequestBody User user, HttpServletRequest request , HttpServletResponse response) {
try {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println("matches ::" + encoder.matches("123", user.getPassword()));
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
final JwtUser userDetails = (JwtUser)authentication.getPrincipal();
SecurityContextHolder.getContext().setAuthentication(authentication);
final String token = jwtTokenUtil.generateToken(userDetails);
response.setHeader("Token", token);
return new ResponseEntity<UserDTO>(new UserDTO(userDetails.getUser(), token) , HttpStatus.OK);
}catch(UnauthorizedException ex) {
ex.printStackTrace();
throw new UnauthorizedException(ex.getMessage());
}
}
}
On calling http://localhost:8080/login from postman and passing the correct email and password , we are getting the following exception
org.springframework.security.authentication.LockedException: User account is locked
Please advice
The message says "User account is locked". This happens after a number of failed authentication events. The account eventually becomes unlocked depending on implementation.
There are only two places that happens in Spring Security:
AccountStatusUserDetailsChecker.check(UserDetails user)
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
throw new LockedException(messages.getMessage(
"AccountStatusUserDetailsChecker.locked", "User account is locked"));
}
if (!user.isEnabled()) {
throw new DisabledException(messages.getMessage(
"AccountStatusUserDetailsChecker.disabled", "User is disabled"));
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException(
messages.getMessage("AccountStatusUserDetailsChecker.expired",
"User account has expired"));
}
if (!user.isCredentialsNonExpired()) {
throw new CredentialsExpiredException(messages.getMessage(
"AccountStatusUserDetailsChecker.credentialsExpired",
"User credentials have expired"));
}
}
AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks.check(UserDetails user)
So if you want to set a breakpoint, that's where you start.
All of this happens in your UserDetailsService which you have in your configuration.
#Autowired private UserDetailsService userDetailsService;
This service returns an object that implements the UserDetails interface
public interface UserDetails {
boolean isAccountNonLocked();
}
if this method returns false, the account is locked. the name is a bit confusing.
Since we don't know what your UserDetailsService is, we can't tell you how this gets populated. So the recommendation is to just set a break point when the error is thrown.
If you don't want the account locking feature to be enabled, there are different ways to implement that. If you override the UserDetailsService bean you can always return users that are never locked.
Another way is to inject your own checker
DaoAuthenticationProvider daoProvider = ....
daoProvider.setPreAuthenticationChecks(toCheck -> {});
There is also a PostAuthenticationChecks object to see if your password has expired.
Related
Currently, I learning security and I'm trying to make a simple login for my Android app. I'm using a custom Filter to request access to my protected data. This way I don't need the default login-page, and use a custom login in my android app instead. But when I do a successful login, I get a 404 response instead of a 200. I there a way to change this as it is set up right now?
MAIN.class
package com.ssl.Oauth2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
Controller.class
#RestController
public class Controller {
#RequestMapping("/protected-resource")
public String protectedResource() {
return "The Protected Resource";
}
#RequestMapping("/public-resource")
public String publicResource() {
return "The Public Resource";
}
}
SecurityConfiguration.class
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("john").password("{noop}12345").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomFilter mupaf = new CustomFilter();
mupaf.setAuthenticationManager(authenticationManager());
http
.csrf().disable()
.addFilterAt(
mupaf,
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/protected-resource").authenticated()
.antMatchers(HttpMethod.POST, "/login").permitAll();
}
}
CustomFilter
package com.ssl.Oauth2;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
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;
// Credentials to Java Map
try {
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = requestMap.get("username");
password = requestMap.get("password");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
// Token
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// TEST
System.out.println("\n------------------------------------------------\n");
System.out.println("AUTHENTICATION REQUEST:\n" +this.getAuthenticationManager().authenticate(authRequest) );
System.out.println("\n------------------------------------------------\n");
// the method authenticate of the AuthenticationManager receives a token
return this.getAuthenticationManager().authenticate(authRequest);
}
}
I am working through several tutorials to implement my own security within my project.
The problem is, as configured the system.out.println calls within the class the extends WebSecurityConfigurerAdapter are not being hit. Which means the security class is not being called at all. There are no error messages and I'm also able to navigate to any page within the site without the authorization redirecting to the login page. Also, the login page just does a post and takes me to the home page of the site.
Here is the custom web security configurer adapter:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
ShopmeUserDetailsService shopmeUserDetailsService;
#Bean
public UserDetailsService userDetailsService() {
return new ShopmeUserDetailsService();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
public DaoAuthenticationProvider authenicationProvider() {
System.out.println("In Dao auth security");
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("In configure security");
//auth.authenticationProvider(authenicationProvider());
//auth.userDetailsService(shopmeUserDetailsService);
auth
.inMemoryAuthentication()
.withUser("user1")
.password(passwordEncoder().encode("user1Pass"))
.roles("USER")
.and()
.withUser("user2")
.password(passwordEncoder().encode("user2Pass"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().encode("adminPass"))
.roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("In configure security auth");
http
.authorizeRequests()
.anyRequest().authenticated() //all URLs are allowed by any authenticated user, no role restrictions.
.and()
.formLogin() //enable form based authentication
.loginPage("/login") //use a custom login URI
.usernameParameter("email")
.permitAll(true) //login URI can be accessed by anyone
.and()
.logout() //default logout handling
.permitAll(); //allow all as it will be accessed when user is not logged in anymore
}
#Override
public void configure(WebSecurity web) throws Exception{
System.out.println("In configure ignorings");
web.ignoring().antMatchers("/images/**", "/js/**", "/webjars/**" );
}
}
Here is the main app class:
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
#EntityScan({"com.shopme.common.entity", "com.shopme.admin.user"})
public class ShopmeBackendApplication {
public static void main(String[] args) {
SpringApplication.run(ShopmeBackendApplication.class, args);
}
}
My main controller:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
#Controller
public class MainController {
#GetMapping("")
public String viewHomePage() {
return "index";
}
#GetMapping("/login")
public String viewLoginPage() {
System.out.println("In viewLoginPage method - MainController");
return "login";
}
#PostMapping("/login")
public String login() {
System.out.println("login attempt");
return "index";
}
}
And finally my other controller for the admin pages:
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.shopme.admin.FileUploadUtil;
import com.shopme.common.entity.Role;
import com.shopme.common.entity.User;
#Controller
public class UserController {
#Autowired
private UserService userService;
//private final java.nio.file.Path root = Paths.get("user_photos");
//Updated method to list the first page of users
#GetMapping("/users")
public String listFirstPage(Model model) {
return listUsersByPage(1, model, null);
}
#GetMapping("/users/new")
public String newUser(Model model) throws IOException {
System.out.println("new User method");
List<Role> roles = userService.listRoles();
//System.out.println(multiPartFile.getOriginalFilename());
//String fileName =
StringUtils.cleanPath(multiPartFile.getOriginalFilename());
//String uploadDir = "user_photos";
//FileUploadUtil.saveFile(uploadDir, fileName, multiPartFile);
//Files.copy(multiPartFile.getInputStream(), ((java.nio.file.Path)
this.root).resolve(multiPartFile.getOriginalFilename()));
User user = new User();
user.setEnabled(true);
model.addAttribute("user", user);
model.addAttribute("roles", roles);
model.addAttribute("pageTitle", "Create New User");
return "user_form";
}
#PostMapping("/users/save")
public String saveUser(User user, RedirectAttributes redirect, #RequestParam("image") MultipartFile multiPartFile) throws IOException {
System.out.println(user);
System.out.println(multiPartFile.getOriginalFilename());
String fileName = StringUtils.cleanPath(multiPartFile.getOriginalFilename());
String uploadDir = "user_photos";
FileUploadUtil.saveFile(uploadDir, fileName, multiPartFile);
//Files.copy(multiPartFile.getInputStream(), ((java.nio.file.Path) this.root).resolve(multiPartFile.getOriginalFilename()));
userService.save(user);
redirect.addFlashAttribute("message", "User has been saved successfully!");
return "redirect:/users/page/1?keyword=" + user.getId();
}
#GetMapping("/users/edit/{id}")
public String editUser(#PathVariable(name = "id") Integer id, Model model, RedirectAttributes redirect){
try {
Optional<User> user = userService.getUserById(id);
List<Role> roles = userService.listRoles();
model.addAttribute("user", user);
model.addAttribute("roles", roles);
model.addAttribute("pageTitle", "Edit User (ID: " + id + ")");
return "user_form";
} catch (UserNotFoundException ex) {
redirect.addFlashAttribute("message", ex.getMessage());
return "redirect:/users";
}
}
#GetMapping("users/delete/{id}")
public String deleteUser(#PathVariable(name="id") Integer id, Model model, RedirectAttributes redirect) {
userService.deleteUserById(id);
redirect.addFlashAttribute("message", "User has been deleted successfully!");
return "redirect:/users";
}
#GetMapping("/users/{id}/enabled/{status}")
public String updateUserEnabledStatus(#PathVariable("id") Integer id, #PathVariable("status") boolean enabled, RedirectAttributes redirect) {
userService.updateUserEdabledStatus(id, enabled);
String status = enabled ? "enabled" : "disabled";
String message = "THe user Id " + id + " has been " + status;
redirect.addFlashAttribute("message", message);
return "redirect:/users";
}
#GetMapping("/users/page/{pageNumber}")
public String listUsersByPage(#PathVariable(name = "pageNumber") int pageNumber, Model model, #Param("keyword") String keyword) {
Page<User> page = userService.listByPage(pageNumber, keyword);
List<User> userPagedList = page.getContent();
System.out.println("Pagenumber: " + pageNumber);
System.out.println("Total Elements: " + page.getTotalElements());
System.out.println("Totals Pages: " + page.getTotalPages());
long startCount = (pageNumber - 1) * UserService.USERS_PER_PAGE +1;
long endCount = startCount + UserService.USERS_PER_PAGE -1;
if(endCount > page.getTotalElements()){
endCount = page.getTotalElements();
}
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("currentPage", pageNumber);
model.addAttribute("startCount", startCount);
model.addAttribute("endCount", endCount);
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("users", userPagedList);
model.addAttribute("keyword", keyword);
return "users";
} //end listUserByPage
#GetMapping("/users/export/csv")
public void exportToCSV(HttpServletResponse response) throws IOException {
List<User> userList = userService.listAll();
UserCsvExporter exporter = new UserCsvExporter();
exporter.export(userList, response);
} //end exportToCsv
#GetMapping("/users/export/excel")
public void exportToExcel(HttpServletResponse response) throws IOException {
List<User> userList = userService.listAll();
UserExcelExporter exporter = new UserExcelExporter();
exporter.export(userList, response);
} //end exportToExcel
#GetMapping("/users/export/pdf")
public void exportToPdf(HttpServletResponse response) throws IOException {
List<User> userList = userService.listAll();
UserPdfExporter exporter = new UserPdfExporter();
exporter.export(userList, response);
} //end exportToPdf
} //end of class
I've spent two days investigating this with no results... any help would greatly be appreciated.
I figured out why this was not working, well at least I have it working with this solution.
I included the WebSecurityConfig.class in the start-up class of the project as shown below:
#SpringBootApplication
#EntityScan({"com.shopme.common.entity", "com.shopme.admin.user"})
public class ShopmeBackendApplication {
public static void main(String[] args) {
SpringApplication.run(new Class[]
{ShopmeBackendApplication.class,
WebSecurityConfig.class}, args);
}
}
I'm trying to implement sso with Spring Security Oauth2 using Spring-boot and Dave Syer samples
I want to use my custom server provider and it's working fine.
For the client, I want user to be authenticate (so redirected to OAuth2 url) when they try to access client site (eg localhost:8080/) and redirect back to index.html file once authenticated. I also want to implement logout when user on a link in index.html file.
I've come up with this following client sso client:
package org.ikane;
import java.io.IOException;
import java.security.Principal;
import java.util.Arrays;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
#SpringBootApplication
#Controller
public class DemoSsoOauth2ClientApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(DemoSsoOauth2ClientApplication.class);
#Override
public void run(String... arg0) throws Exception {
SecurityContext securityContext = SecurityContextHolder.getContext();
try {
Authentication authentication = securityContext.getAuthentication();
logger.info(authentication.getDetails().toString());
SecurityContextHolder.clearContext();
} catch (Exception e) {
logger.error("Error", e);
}
}
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoSsoOauth2ClientApplication.class, args);
ConfigurableEnvironment env = applicationContext.getEnvironment();
logger.info("\n\thttp://localhost:{}{}\n\tProfiles:{}\n",
StringUtils.defaultIfEmpty(env.getProperty("server.port"), "8080"),
StringUtils.defaultIfEmpty(env.getProperty("server.contextPath"), "/"),
Arrays.toString(env.getActiveProfiles()));
}
#RequestMapping(value="/")
public String home() {
return "index";
}
#RequestMapping(value="/user")
#ResponseBody
public Principal user(Principal user) {
return user;
}
/**
* The Class OAuthConfiguration that sets up the OAuth2 single sign on
* configuration and the web security associated with it.
*/
#Component
#Controller
#EnableOAuth2Sso
protected static class OAuthClientConfiguration extends WebSecurityConfigurerAdapter {
private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
private static final String CSRF_ANGULAR_HEADER_NAME = "X-XSRF-TOKEN";
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers("/index.html", "/").permitAll().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 = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie(CSRF_COOKIE_NAME, token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
/**
* Angular sends the CSRF token in a custom header named "X-XSRF-TOKEN"
* rather than the default "X-CSRF-TOKEN" that Spring security expects.
* Hence we are now telling Spring security to expect the token in the
* "X-XSRF-TOKEN" header.
*
* This customization is added to the csrf() filter.
*
* #return
*/
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName(CSRF_ANGULAR_HEADER_NAME);
return repository;
}
}
}
You can find a GitHub source. Any hints on how to implement this use case?
Thanks in advance
To make your client app redirect to the Authorization Server just add
the annotation #EnableOAuth2Sso on your WebSecurityConfigurerAdapter and
place the proper OAuth2 configurations (client-id, secret, access token uri...) in your properties file.
(I'm assuming that your client app is using Spring Boot as well)
To end the user's session you have to redirect to an endpoint in the authorization server and logout programmatically as shown in this post.
I have created a repository on github with a sample app that has those features that you are looking for.
Please check it out and let me know if it helps you.
I'm trying to populate the database with a hashed password and then log in to my application, by matching the data I'm submitting through my log in form, just like how a typical hashed password is suppose to work. I'm using spring security and spring boot, and so far I know that the log in form is working because I get the error Encoded password does not look like BCrypt. And I know that when I'm submitting the user to the database it's not working because I just see a plain string in the password column in the database. I'm really not sure where I'm going wrong here.
Here's my User object
package com.example.objects;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import com.example.security.PasswordCrypto;
import com.example.security.RoleEnum;
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "termsOfService")
private Boolean termsOfService;
#OneToMany(mappedBy = "user")
private Set<UserRole> roles;
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<QuestionAnswerSet> questionAnswerSet;
public static User createUser(String username, String email, String password) {
User user = new User();
user.username = username;
user.email = email;
user.password = PasswordCrypto.getInstance().encrypt(password);
if(user.roles == null) {
user.roles = new HashSet<UserRole>();
}
//create a new user with basic user privileges
user.roles.add(
new UserRole(
RoleEnum.USER.toString(),
user
));
return user;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getTermsOfService() {
return termsOfService;
}
public void setTermsOfService(Boolean termsOfService) {
this.termsOfService = termsOfService;
}
public Set<QuestionAnswerSet> getQuestionAnswerSet() {
return questionAnswerSet;
}
public void setQuestionAnswerSet(Set<QuestionAnswerSet> questionAnswerSet) {
this.questionAnswerSet = questionAnswerSet;
}
public Set<UserRole> getRoles() {
return roles;
}
public void setRoles(Set<UserRole> roles) {
this.roles = roles;
}
}
Here's my Security Config
package com.example.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
#Autowired
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(csrfTokenRepository());
http
.authorizeRequests()
.antMatchers("/","/home","/register", "/result").permitAll()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
if(encoder == null) {
encoder = new BCryptPasswordEncoder();
}
return encoder;
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setSessionAttributeName("_csrf");
return repository;
}
}
My user detail service
package com.example.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.dao.UserDao;
import com.example.objects.UserRole;
#Service
#Qualifier("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Transactional
#Override
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException {
com.example.objects.User user = userDao.findByUsername(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.example.objects.User user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRoleName()));
}
return new ArrayList<GrantedAuthority>(setAuths);
}
}
And PasswordCrypto
package com.example.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
public class PasswordCrypto {
#Autowired
private PasswordEncoder passwordEncoder;
private static PasswordCrypto instance;
public static PasswordCrypto getInstance() {
if(instance == null) {
instance = new PasswordCrypto();
}
return instance;
}
public String encrypt(String str) {
return passwordEncoder.encode(str);
}
}
If anyone knows what I'm doing wrong and could help me out, that would be awesome, also let me know if I need to show anymore code. Thanks in advance.
Use encoder to user repository like this :
public class UserRepositoryService implements UserService {
private PasswordEncoder passwordEncoder;
private UserRepository repository;
#Autowired
public UserRepositoryService(PasswordEncoder passwordEncoder,
UserRepository repository) {
this.passwordEncoder = passwordEncoder;
this.repository = repository;
}
private boolean emailExist(String email) {
User user = repository.findByEmail(email);
if (user != null) {
return true;
}
return false;
}
private String encodePassword(RegistrationForm dto) {
String encodedPassword = null;
if (dto.isNormalRegistration()) {
encodedPassword = passwordEncoder.encode(dto.getPassword());
}
return encodedPassword;
}
#Transactional
#Override
public User registerNewUserAccount(RegistrationForm userAccountData)
throws DuplicateEmailException {
if (emailExist(userAccountData.getEmail())) {
LOGGER.debug("Email: {} exists. Throwing exception.",
userAccountData.getEmail());
throw new DuplicateEmailException("The email address: "
+ userAccountData.getEmail() + " is already in use.");
}
String encodedPassword = encodePassword(userAccountData);
User.Builder user = User.getBuilder().email(userAccountData.getEmail())
.firstName(userAccountData.getFirstName())
.lastName(userAccountData.getLastName())
.password(encodedPassword)
.background(userAccountData.getBackground())
.purpose(userAccountData.getPurpose());
if (userAccountData.isSocialSignIn()) {
user.signInProvider(userAccountData.getSignInProvider());
}
User registered = user.build();
return repository.save(registered);
}
}
For morre info, check out this repo
https://bitbucket.org/sulab/biobranch/src/992791aa706d0016de8634ebb6347a81fe952c24/src/main/java/org/scripps/branch/entity/User.java?at=default&fileviewer=file-view-default
My problem was that I needed to add user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword())); in my UserController Post method right before I saved the user
I'm writing my own implementation of TokenStore (org.springframework.security.oauth2.provider.token.TokenStore) using MongoDB. There seems to be some problem with converting/mapping of the object in the database back to Java object.
Anyone have a clue how I could solve this?
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate org.springframework.security.authentication.UsernamePasswordAuthenticationToken using constructor NO_CONSTRUCTOR with arguments
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:64) ~[spring-data-commons-1.11.0.RELEASE.jar:na]
at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:83) ~[spring-data-commons-1.11.0.RELEASE.jar:na]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:251) ~[spring-data-mongodb-1.8.0.RELEASE.jar:na]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:231) ~[spring-data-mongodb-1.8.0.RELEASE.jar:na]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1185) ~[spring-data-mongodb-1.8.0.RELEASE.jar:na]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:78) ~[spring-data-mongodb-1.8.0.RELEASE.jar:na]
You need to create a converter and register it as Spring Mongo doesn't do this for you.
Create a converter
import com.erranda.abraham.entity.Person;
import com.mongodb.DBObject;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import java.util.*;
/**
* #version 1.0
* #author: Iain Porter
* #since 23/05/2013
*/
//Hackery to deserialize back into an OAuth2Authentication Object made necessary because Spring Mongo can't map clientAuthentication to authorizationRequest
#ReadingConverter
#SuppressWarnings("rawtypes")
public class OAuth2AuthenticationReadConverter implements Converter<DBObject, OAuth2Authentication> {
#Override
#SuppressWarnings("unchecked")
public OAuth2Authentication convert(DBObject source) {
System.out.println(source);
DBObject storedRequest = (DBObject)source.get("storedRequest");
OAuth2Request oAuth2Request = new OAuth2Request((Map<String, String>)storedRequest.get("requestParameters"),
(String)storedRequest.get("clientId"), null, true, new HashSet((List)storedRequest.get("scope")),
null, null, null, null);
DBObject userAuthorization = (DBObject)source.get("userAuthentication");
Object principal = getPrincipalObject(userAuthorization.get("principal"));
Authentication userAuthentication = new UsernamePasswordAuthenticationToken(principal,
(String)userAuthorization.get("credentials"), getAuthorities((List) userAuthorization.get("authorities")));
OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request,
userAuthentication );
return authentication;
}
private Object getPrincipalObject(Object principal) {
if(principal instanceof DBObject) {
DBObject principalDBObject = (DBObject)principal;
Person user = new Person (principalDBObject);
return user;
} else {
return principal;
}
}
private Collection<GrantedAuthority> getAuthorities(List<Map<String, String>> authorities) {
Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>(authorities.size());
for(Map<String, String> authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.get("role")));
}
return grantedAuthorities;
}
}
Then you need to register converter along side your mongodb configuration
import com.erranda.abraham.api.security.OAuth2AuthenticationReadConverter;
import com.mongodb.Mongo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.authentication.UserCredentials;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
#Configuration
public class MongoDbConfiguration extends AbstractMongoConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(MongoDbConfiguration.class);
private static final String MONGO_DB_SERVER = "mongo.db.server";
private static final String MONGO_DB_PORT = "mongo.db.port";
private static final String MONGO_DB_NAME = "mongo.db.name";
private static final String MONGO_DB_LOGON = "mongo.db.logon";
private static final String MONGO_DB_PASSWORD = "mongo.db.password";
private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";
#Autowired
private ApplicationContext applicationContext;
#Value("${" + MONGO_DB_SERVER + "}")
private String mongoServer;
#Value("${" + MONGO_DB_PORT + "}")
private int mongoPort;
#Value("${" + MONGO_DB_NAME + "}")
private String mongoDBName;
#Value("${" + MONGO_DB_LOGON + "}")
private String mongoDbLogin;
#Value("${" + MONGO_DB_PASSWORD + "}")
private String mongoDbPassword;
#Override
protected String getDatabaseName() {
return mongoDBName;
}
#Override
#Bean
public Mongo mongo() throws Exception {
return new Mongo(mongoServer, mongoPort);
}
#Override
#Bean
public MongoTemplate mongoTemplate() throws Exception {
if (!StringUtils.isEmpty(mongoDbLogin)) {
LOG.info("Configuring mongoTemplate with credentials.");
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongo(), mongoDBName, new UserCredentials(mongoDbLogin, mongoDbPassword));
return new MongoTemplate(mongoDbFactory, mappingMongoConverter());
} else {
LOG.info("Configuring mongoTemplate without credentials.");
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongo(), mongoDBName);
return new MongoTemplate(mongoDbFactory, mappingMongoConverter());
}
}
#Override
#Bean
public CustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<Converter<?, ?>>();
OAuth2AuthenticationReadConverter converter = new OAuth2AuthenticationReadConverter();
converterList.add(converter);
return new CustomConversions(converterList);
}
private String getContextProperty(final String propertyKey) {
return applicationContext.getEnvironment().getProperty(propertyKey);
}
}
Don't forget to mark as correct if it works for you.
Based on https://github.com/iainporter/oauth2-provider