Contents of property source files are not recognized in some classes - spring-boot

#Value anotation does not get the property from properties file when called from a specific class.
PropertiesConfig:
#Configuration
#PropertySource(value = "classpath:s_app.properties")
#PropertySource(value = "classpath:sDBpools.properties")
public class PropertiesConfig {
}
When I call property from ProjectInfoServiceImpl class it returns but not works from JwtTokenUtil class.
secret returns null here:
#Component
#Log4j2
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -1L;
private static final int EXPIRATION_MILLISECONDS = 60 * 60 * 1000;
#Value("${info.app.jwt.secret}")
private String secret;
public String generateToken(User user){
Map<String, Object> claims = new HashMap<>();
claims.put("authorities", user.getAuthorities());
return generateToken(user.getUsername(), claims);
}
public String generateToken(UserDetails userDetails){
Map<String, Object> claims = new HashMap<>();
claims.put("authorities", userDetails.getAuthorities());
return generateToken(userDetails.getUsername(), claims);
}
public String generateToken(String username, Map<String, Object> claims) {
return Jwts.builder()
.setSubject(username)
.setClaims(claims)
.setExpiration(new Date((System.currentTimeMillis() + EXPIRATION_MILLISECONDS)))
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public boolean validateToken(String username, String token){
return (username.equals(getUsernameFromToken(token)) && !isExpired(token));
}
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationdateFromToken(String token){
return getClaimFromToken(token, Claims::getExpiration);
}
public boolean isExpired(String token){
return !(new Date().before(getExpirationdateFromToken(token)));
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
}
But in here it returns as expected:
#Service
#Log4j2
public class ProjectInfoServiceImpl implements ProjectInfoService {
#Value("${info.app.jwt.secret}")
private String secret;
#Value("${s.version}")
private String version;
#Value("${s.deployment.profile}")
private String deploymentProfile;
#Value("${s.deployment.environment}")
private String deploymentEnvironment;
#Override
public ProjectInfoDTO getProjectInfo() {
ProjectInfoDTO projectInfoDTO = new ProjectInfoDTO();
projectInfoDTO.setVersion(version);
projectInfoDTO.setDeploymentProfile(deploymentProfile);
projectInfoDTO.setDeploymentEnvironment(deploymentEnvironment);
return projectInfoDTO;
}
}
Here is folder structure:
Besides actuator does not get info from info.app.version property of s_properties file. /info endpoint returns no information since it cant find the property.

Problem solved with the approved answer at below question.
I had manually instantiated JwtTokenUtil class instead of autowiring it in the caller class. Probably I was not reaching proxy object at all.
Unable to read properties using #Value in SpringBoot application

Related

Use of jwt for authentication and signup on springboot webflux and microservice with user in database

I am writing a microservice project with springboot that require jwt auth for spring webflux security for user in database .I have idea on how to implement jwt on basic jwt authentication on normal spring security,but it fails when applying to webflux.
The main problem is that i have no idea how to turn the entity class to "mono" class.
My tokenservice responsible for signup:
public String signup(SignupDto user){
Mono<User> saveduser=webclient.baseUrl("http://USER").build().post()
.uri("/User/adduser/")
.header(MediaType.APPLICATION_JSON_VALUE)
.body(Mono.just(
new User(
0, user.getUsername(),
passwordEncoder.encode(user.getPassword()),
user.getFullName(),
user.getEmail(),
user.getSkill_set(),user.getContact(),user.getCv(),user.getAddress_id()
,user.getAddress(),null,3
)
),User.class)
.retrieve()
.bodyToMono(User.class);
//make syn request
Mono<Authentication> authentication= reactiveAuthenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUsername()
,user.getPassword())
);
return generateToken( authentication);
}
generate token function:
public String generateToken(Authentication authentication){
Instant now=Instant.now();
String scope=authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(" "));
String secret="AMDM:LM:LSMLdfsf";
JwtClaimsSet claims=JwtClaimsSet.builder()
.issuer("http://localhost:8080")
.issuedAt(now)
.expiresAt(now.plus(2, ChronoUnit.HOURS))
.subject(authentication.getName())
.claim("scope",scope)
.claim("secret",secret)
.build();
return this.jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
The webclient is calling the user at the other microservice which the api should send back the "user" class.The problem is that i cant cast the Mono class in Mono to normal authentication.
User entity:
#Data
#AllArgsConstructor
#NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
private String fullName;
private String email;
private String skill_set;
private String contact;
private String cv;
private int Address_id;
private String address;
private String role;
private double score=3.0;
The reactiveauthenticationManager bean:
#Bean
protected ReactiveAuthenticationManager reactiveAuthenticationManager() {
log.info("Received authentication request");
return authentication -> {
UserDetailsRepositoryReactiveAuthenticationManager authenticator = new UserDetailsRepositoryReactiveAuthenticationManager(securityUserService);
authenticator.setPasswordEncoder(passwordEncoder());
return authenticator.authenticate(authentication);
};
}
The SecurityUserService:
#Service
public class SecurityUserService implements ReactiveUserDetailsService {
#Autowired
private WebClient.Builder webClientBuilder;
#Override
public Mono<UserDetails> findByUsername(String username) {
return webClientBuilder.baseUrl("http://USER").build().get()
.uri(uriBuilder -> uriBuilder
.path("/User/AuthUser/{username}")
.build(username))
.retrieve()
.bodyToMono(UserAuthdto.class)
.map(
SecurityUser::new
//res->new SecurityUser(res)
)
SecurityUser:
public class SecurityUser implements UserDetails {
private final UserAuthdto user;
public SecurityUser(UserAuthdto user) {
this.user = user;
}
public int getID(){
return user.getId();
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.stream(user
.getRole()
.split(","))
.map(SimpleGrantedAuthority::new)
.toList();}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Passwword Encoder:
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
The api at the other microservice are tested and should fine.Please help to see any problem on my code.Any clue is also fine.

AuthenticationManager returning Bad Credentials

I have this error and I don't know what it could be. I'm following a tutorial to do the User authentication, But when I try to do the authentication it throws a "Bad credentials" but the credentials are right. I'm using MongoDB.
From the tutorial I'm following with these steps it should work (other users confirmed that they did) but the only thing I did differently from the tutorial is in the "ClientEntity" class that already existed and I just implemented what I needed.
User return:
ClientEntity(id=63166ddbe3ea6c4fffd70818, clientName=Test, clientCpf=000.000.000-00, clientEmail=teste2#example.com, clientPassword=2b598e4c0e79baf9dc9211ad303e7626, clientIsBlocked=false, clientBirthDate=1989-05-20, creditCards=[CreditCardEntity()], clientCategory=[ACAO, COMEDIA])
My request to signin, I'm logging in by email and password:
AccountCredentials: AccountCredentials(email=teste2#example.com, password=2b598e4c0e79baf9dc9211ad303e7626)
I know the problem is in that class in "authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(email, password));" but I don't know how to solve it, because the credentials are correct
My AuthService class:
#Service
public class AuthService {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private ClientRepository repository;
public ResponseEntity signin(AccountCredentials data) {
try {
var email = data.getEmail();
var password = data.getPassword();
log.info("METODO SIGNIN, AccountCredentials: " + data);
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(email, password));
log.info("FOI AUTENTICADO!");
var user = repository.findByClientEmail(email);
log.info("O valor de User é: " + user);
var tokenResponse = new Token();
if (user != null) {
tokenResponse = tokenProvider.createAccessToken(email, user.getRoles());
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
return ResponseEntity.ok(tokenResponse);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getCause());
System.out.println(e.getLocalizedMessage());
throw new BadCredentialsException("Invalid email/password supplied!");
}
}
public ResponseEntity refreshToken(String email, String refreshToken) {
var user = repository.findByClientEmail(email);
var tokenResponse = new Token();
if (user != null) {
tokenResponse = tokenProvider.refreshToken(refreshToken);
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
return ResponseEntity.ok(tokenResponse);
}
}
My ClientEntity:
#Data
#Document(collection = "Client")
public class ClientEntity implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
private String clientName;
private String clientCpf;
private String clientEmail;
private String clientPassword;
private boolean clientIsBlocked = false;
private LocalDate clientBirthDate;
private List<CreditCardEntity> creditCards;
private List<ClientCategoryEnum> clientCategory;
private List<Permission> permissions;
public List<String> getRoles() {
List<String> roles = new ArrayList<>();
for (Permission permission : permissions) {
roles.add("USER");
roles.add(permission.getDescription());
}
return roles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.permissions;
}
#Override
public String getPassword() {
return this.clientPassword;
}
#Override
public String getUsername() {
return this.clientEmail;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
My Class Permission:
#Data
#Document(collection = "Roles")
public class Permission implements GrantedAuthority, Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String description;
#Override
public String getAuthority() {
return this.description;
}
}
My SecurityConfig:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenProvider tokenProvider;
/*
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
*/
#Bean
public PasswordEncoder passwordEncoder() {
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("pbkdf2", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(new Pbkdf2PasswordEncoder());
return passwordEncoder;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(
"/auth/signin",
"/auth/refresh"
).permitAll()
.antMatchers("/api/movie_search/**").authenticated()
.and()
.cors()
.and()
.apply(new JwtConfigurer(tokenProvider));
}
}
My CLass TokenProvider:
#Service
public class JwtTokenProvider{
#Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
#Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
#Autowired
private UserDetailsService userDetailsService;
Algorithm algorithm = null;
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
algorithm = Algorithm.HMAC256(secretKey.getBytes());
}
public Token createAccessToken(String email, List<String> roles) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
var accessToken = getAccessToken(email, roles, now, validity);
var refreshToken = getRefreshToken(email, roles, now);
return new Token(email, true, now, validity, accessToken, refreshToken);
}
public Token refreshToken(String refreshToken) {
if (refreshToken.contains("Bearer ")) refreshToken =
refreshToken.substring("Bearer ".length());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(refreshToken);
String email = decodedJWT.getSubject();
List<String> roles = decodedJWT.getClaim("roles").asList(String.class);
return createAccessToken(email, roles);
}
private String getAccessToken(String email, List<String> roles, Date now, Date validity) {
String issuerUrl = ServletUriComponentsBuilder
.fromCurrentContextPath().build().toUriString();
return JWT.create()
.withClaim("roles", roles)
.withIssuedAt(now)
.withExpiresAt(validity)
.withSubject(email)
.withIssuer(issuerUrl)
.sign(algorithm)
.strip();
}
private String getRefreshToken(String email, List<String> roles, Date now) {
Date validityRefreshToken = new Date(now.getTime() + (validityInMilliseconds * 3));
return JWT.create()
.withClaim("roles", roles)
.withIssuedAt(now)
.withExpiresAt(validityRefreshToken)
.withSubject(email)
.sign(algorithm)
.strip();
}
public Authentication getAuthentication(String token) {
DecodedJWT decodedJWT = decodedToken(token);
UserDetails userDetails = this.userDetailsService
.loadUserByUsername(decodedJWT.getSubject());
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
private DecodedJWT decodedToken(String token) {
Algorithm alg = Algorithm.HMAC256(secretKey.getBytes());
JWTVerifier verifier = JWT.require(alg).build();
DecodedJWT decodedJWT = verifier.verify(token);
return decodedJWT;
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring("Bearer ".length());
}
return null;
}
public boolean validateToken(String token) {
DecodedJWT decodedJWT = decodedToken(token);
try {
if (decodedJWT.getExpiresAt().before(new Date())) {
return false;
}
return true;
} catch (Exception e) {
throw new InvalidJwtAuthenticationException("Expired or invalid JWT token!");
}
}
}
My ClientService:
#Service
public class ClientService implements UserDetailsService {
private final ClientRepository clientRepository;
private final ModelMapper modelMapper;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
var user = clientRepository.findByClientEmail(email);
if (user != null) {
return user;
} else {
throw new UsernameNotFoundException("Email " + email + " not found!");
}
}
}
In debug I came across this:
UsernamePasswordAuthenticationToken
Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]
Short answer: Passwords stored in your DB must be encrypted by PasswordEncoder.
Long answer:
You created a bean of type PasswordEncoder, so it is used by AuthenticationProvider when you call .authenticate() method of AuthenticationManager class.
As stated in java docs, DelegatingPasswordEncoder expects your passwords to be stored in DB in format like this:
{encoderId}encryptedPassword
You use pbkdf2 password encoder as a default encoder, so passwords in DB should look like this:
{pbkdf2}encryptedPassword
By the look of the returned ClientEntity, you haven't used the PasswordEncoder when you saved this entity to a DB, so the raw password is stored instead of an encrypted one.
The authentication flow should look like this:
your app registers a user, stores his password in DB in encrypted form using PasswordEncoder bean;
user passes his username (email or whatever is neccessary) and raw password to your app through login form of API endpoint;
AuthenticationProvider retrieves encrypted password from the DB and compares it with the raw password the user has provided

org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser cannot be cast to model.UserPrincipal

I'm working with oauth2login in spring security. The redirection to the provider (facebook or google) was made with success but I'm getting the error : org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser cannot be cast to model.UserPrincipal, in this line of code
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
My UserPrincipal class is like this :
public class UserPrincipal implements OAuth2User, OidcUser, UserDetails {
private Long id;
private String email;
private String password;
private Collection<? extends GrantedAuthority> authorities;
private Map<String, Object> attributes;
private Map<String, Object> claims;
private OidcUserInfo oidcUserInfo;
private OidcIdToken oidcIdToken;
...
}
How can I resolve this issue please?
Have You Override methods in UserPrincipal class that implements OAuth2User, UserDetails.
#Override
public String getName() {
return String.valueOf(id);
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}

Post json data and file using ResponseEntity<>

I am trying to upload json data and image to a database of a form using spring rest and hibernate. I tried to test it using POSTMAN by setting form-data in body and content-type as application/json in header, but i am getting http error 400. I also tried using #RequestPart but didnt not work. I searched but could not find an example using ResponseEnity<>. I think i am doing something wrong in controller class. Please someone help me.
Without the file part i am able to add json data to db using this.
#RequestMapping(value = "/users", method = RequestMethod.POST, produces ="application/json")
public ResponseEntity<User> createAparts( #RequestBody User user) {
if (user == null) {
return new ResponseEntity<User>(HttpStatus.BAD_REQUEST);
}
userService.addAparts(user);
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
Below are the related code to issue.
model
#Entity
#Table(name = "User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "ignoreUnknown = true"})
public class User{
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "Name")
private String Name;
#Column(name = "file_data")
private byte[] file_data;
#Column(name = "filename")
private String filename;
#JsonCreator
public ApartsData(#JsonProperty("id") int id,
#JsonProperty("Name") String Name,
#JsonProperty("filename") String filename,
#JsonProperty("file_data") byte[] file_data){
this.ad_id = ad_id;
this.Name = Name;
this.filename= filename;
this.file_data = file_data;
}
public User(){
}
DAO
#Repository
public class UserDaoImpl implements UserDao{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
public void addUser(User user) {
Session session = this.sessionFactory.getCurrentSession();
session.persist(user);
}
}
Service
#Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#Override
#Transactional
public void addUser(User user) {
this.userDao.addUser(user);
}
}
controller
#RestController
public class UserController {
private UserService userService;
#Autowired(required=true)
#Qualifier(value="userService")
public void setUserService(UserService userService){
this.userService = userService;
}
#RequestMapping(value = "/users", method = RequestMethod.POST,
produces ="application/json")
public ResponseEntity<User> createApartsData(#RequestBody User user,
#RequestParam("file") MultipartFile file) {
HttpHeaders headers = new HttpHeaders();
if (user == null) {
return new ResponseEntity<User>(HttpStatus.BAD_REQUEST);
}
if (!file.isEmpty()) {
try {
user.setFilename(file.getOriginalFilename());
user.setFile_data(file.getBytes());
} catch (Exception e){
e.printStackTrace();
}
}
userService.addUser(user);
headers.add("User Created - ", String.valueOf(user.getid()));
return new ResponseEntity<User>(user, headers, HttpStatus.CREATED);
}
}
UPDATE:
I am able to make it work with #RequestParam. Please some help me to make it work with #RequestBody

How to use OAuth2RestTemplate?

I'm trying to understand how to use a OAuth2RestTemplate object to consume my OAuth2 secured REST service (which is running under a different project and let's assume also on a different server etc...)
An example of my REST service is:
http://localhost:8082/app/helloworld
-> Accessing this URL generates an error as I am not authenticated
To request a token I would go to:
http://localhost:8082/app/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=**USERNAME**&password=**PASSWORD**
After I receive the token I can then connect to the REST API by using the following URL (example token inserted)
http://localhost:8082/app/helloworld/?access_token=**4855f557-c6ee-43b7-8617-c24591965206**
Now my question is how do I implement a second application which can consume this OAuth2 secured REST API? I really haven't found any working examples where you provide the user name and password (e.g. coming from a login form) and then a token is generated which can be re-used to get data from the REST API.
I currently tried something with the following objects:
BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails = new BaseOAuth2ProtectedResourceDetails();
baseOAuth2ProtectedResourceDetails.setClientId("restapp");
baseOAuth2ProtectedResourceDetails.setClientSecret("restapp");
baseOAuth2ProtectedResourceDetails.setGrantType("password");
// how to set user name and password ???
DefaultAccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
OAuth2ClientContext oAuth2ClientContext = new DefaultOAuth2ClientContext(accessTokenRequest());
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(baseOAuth2ProtectedResourceDetails,oAuth2ClientContext);
But this just isn't working :(
Any ideas, links to working examples or tutorials are greatly appreciated.
You can find examples for writing OAuth clients here:
https://github.com/spring-projects/spring-security-oauth
In your case you can't just use default or base classes for everything, you have a multiple classes Implementing OAuth2ProtectedResourceDetails. The configuration depends of how you configured your OAuth service but assuming from your curl connections I would recommend:
#EnableOAuth2Client
#Configuration
class MyConfig{
#Value("${oauth.resource:http://localhost:8082}")
private String baseUrl;
#Value("${oauth.authorize:http://localhost:8082/oauth/authorize}")
private String authorizeUrl;
#Value("${oauth.token:http://localhost:8082/oauth/token}")
private String tokenUrl;
#Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource;
resource = new ResourceOwnerPasswordResourceDetails();
List scopes = new ArrayList<String>(2);
scopes.add("write");
scopes.add("read");
resource.setAccessTokenUri(tokenUrl);
resource.setClientId("restapp");
resource.setClientSecret("restapp");
resource.setGrantType("password");
resource.setScope(scopes);
resource.setUsername("**USERNAME**");
resource.setPassword("**PASSWORD**");
return resource;
}
#Bean
public OAuth2RestOperations restTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
}
}
#Service
#SuppressWarnings("unchecked")
class MyService {
#Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
Do not forget about #EnableOAuth2Client on your config class, also I would suggest to try that the urls you are using are working with curl first, also try to trace it with the debugger because lot of exceptions are just consumed and never printed out due security reasons, so it gets little hard to find where the issue is. You should use logger with debug enabled set.
Good luck
I uploaded sample springboot app on github https://github.com/mariubog/oauth-client-sample
to depict your situation because I could not find any samples for your scenario .
In the answer from #mariubog (https://stackoverflow.com/a/27882337/1279002) I was using password grant types too as in the example but needed to set the client authentication scheme to form. Scopes were not supported by the endpoint for password and there was no need to set the grant type as the ResourceOwnerPasswordResourceDetails object sets this itself in the constructor.
...
public ResourceOwnerPasswordResourceDetails() {
setGrantType("password");
}
...
The key thing for me was the client_id and client_secret were not being added to the form object to post in the body if resource.setClientAuthenticationScheme(AuthenticationScheme.form); was not set.
See the switch in:
org.springframework.security.oauth2.client.token.auth.DefaultClientAuthenticationHandler.authenticateTokenRequest()
Finally, when connecting to Salesforce endpoint the password token needed to be appended to the password.
#EnableOAuth2Client
#Configuration
class MyConfig {
#Value("${security.oauth2.client.access-token-uri}")
private String tokenUrl;
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Value("${security.oauth2.client.password-token}")
private String passwordToken;
#Value("${security.user.name}")
private String username;
#Value("${security.user.password}")
private String password;
#Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(tokenUrl);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setUsername(username);
resource.setPassword(password + passwordToken);
return resource;
}
#Bean
public OAuth2RestOperations restTemplate() {
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
}
}
#Service
#SuppressWarnings("unchecked")
class MyService {
#Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
I have different approach if you want access token and make call to other resource system with access token in header
Spring Security comes with automatic security: oauth2 properties access from application.yml file for every request and every request has SESSIONID which it reads and pull user info via Principal, so you need to make sure inject Principal in OAuthUser and get accessToken and make call to resource server
This is your application.yml, change according to your auth server:
security:
oauth2:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
#Component
public class OAuthUser implements Serializable {
private static final long serialVersionUID = 1L;
private String authority;
#JsonIgnore
private String clientId;
#JsonIgnore
private String grantType;
private boolean isAuthenticated;
private Map<String, Object> userDetail = new LinkedHashMap<String, Object>();
#JsonIgnore
private String sessionId;
#JsonIgnore
private String tokenType;
#JsonIgnore
private String accessToken;
#JsonIgnore
private Principal principal;
public void setOAuthUser(Principal principal) {
this.principal = principal;
init();
}
public Principal getPrincipal() {
return principal;
}
private void init() {
if (principal != null) {
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
if (oAuth2Authentication != null) {
for (GrantedAuthority ga : oAuth2Authentication.getAuthorities()) {
setAuthority(ga.getAuthority());
}
setClientId(oAuth2Authentication.getOAuth2Request().getClientId());
setGrantType(oAuth2Authentication.getOAuth2Request().getGrantType());
setAuthenticated(oAuth2Authentication.getUserAuthentication().isAuthenticated());
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication
.getDetails();
if (oAuth2AuthenticationDetails != null) {
setSessionId(oAuth2AuthenticationDetails.getSessionId());
setTokenType(oAuth2AuthenticationDetails.getTokenType());
// This is what you will be looking for
setAccessToken(oAuth2AuthenticationDetails.getTokenValue());
}
// This detail is more related to Logged-in User
UsernamePasswordAuthenticationToken userAuthenticationToken = (UsernamePasswordAuthenticationToken) oAuth2Authentication.getUserAuthentication();
if (userAuthenticationToken != null) {
LinkedHashMap<String, Object> detailMap = (LinkedHashMap<String, Object>) userAuthenticationToken.getDetails();
if (detailMap != null) {
for (Map.Entry<String, Object> mapEntry : detailMap.entrySet()) {
//System.out.println("#### detail Key = " + mapEntry.getKey());
//System.out.println("#### detail Value = " + mapEntry.getValue());
getUserDetail().put(mapEntry.getKey(), mapEntry.getValue());
}
}
}
}
}
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public boolean isAuthenticated() {
return isAuthenticated;
}
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
public Map<String, Object> getUserDetail() {
return userDetail;
}
public void setUserDetail(Map<String, Object> userDetail) {
this.userDetail = userDetail;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
#Override
public String toString() {
return "OAuthUser [clientId=" + clientId + ", grantType=" + grantType + ", isAuthenticated=" + isAuthenticated
+ ", userDetail=" + userDetail + ", sessionId=" + sessionId + ", tokenType="
+ tokenType + ", accessToken= " + accessToken + " ]";
}
#RestController
public class YourController {
#Autowired
OAuthUser oAuthUser;
// In case if you want to see Profile of user then you this
#RequestMapping(value = "/profile", produces = MediaType.APPLICATION_JSON_VALUE)
public OAuthUser user(Principal principal) {
oAuthUser.setOAuthUser(principal);
// System.out.println("#### Inside user() - oAuthUser.toString() = " + oAuthUser.toString());
return oAuthUser;
}
#RequestMapping(value = "/createOrder",
method = RequestMethod.POST,
headers = {"Content-type=application/json"},
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public FinalOrderDetail createOrder(#RequestBody CreateOrder createOrder) {
return postCreateOrder_restTemplate(createOrder, oAuthUser).getBody();
}
private ResponseEntity<String> postCreateOrder_restTemplate(CreateOrder createOrder, OAuthUser oAuthUser) {
String url_POST = "your post url goes here";
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", String.format("%s %s", oAuthUser.getTokenType(), oAuthUser.getAccessToken()));
headers.add("Content-Type", "application/json");
RestTemplate restTemplate = new RestTemplate();
//restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<String> request = new HttpEntity<String>(createOrder, headers);
ResponseEntity<String> result = restTemplate.exchange(url_POST, HttpMethod.POST, request, String.class);
System.out.println("#### post response = " + result);
return result;
}
}
My simple solution. IMHO it's the cleanest.
First create a application.yml
spring.main.allow-bean-definition-overriding: true
security:
oauth2:
client:
clientId: XXX
clientSecret: XXX
accessTokenUri: XXX
tokenName: access_token
grant-type: client_credentials
Create the main class: Main
#SpringBootApplication
#EnableOAuth2Client
public class Main extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll();
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#Bean
public OAuth2RestTemplate oauth2RestTemplate(ClientCredentialsResourceDetails details) {
return new OAuth2RestTemplate(details);
}
}
Then Create the controller class: Controller
#RestController
class OfferController {
#Autowired
private OAuth2RestOperations restOperations;
#RequestMapping(value = "/<your url>"
, method = RequestMethod.GET
, produces = "application/json")
public String foo() {
ResponseEntity<String> responseEntity = restOperations.getForEntity(<the url you want to call on the server>, String.class);
return responseEntity.getBody();
}
}
Maven dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
</dependencies>

Resources