How to perform authorization with external authenticate - spring-security

Im applying Spring Security for my Spring Boot application, for the part authentication, i have 3rd party to do it, basically, when user access website, the gateway will navigate user to a central login platform to do authenticate. If user login successfully, a special key will be append for every later HttpServletRequest.
From my Controller, i just need to extract authenticated user from request.
So now i have question, if i want to use Authorization feature from Spring Security, how do i inject the user info (username, email) and role from request to spring authorization process?
Because i want to validate user access to some specific url, like /admin/** only can access by role admin
i see we can create user detail like this:
public class SecurityUser implements UserDetails{
String ROLE_PREFIX = "ROLE_";
String userName;
String password;
String role;
public SecurityUser(String username, String password, String role){
this.userName = username;
this.password = password;
this.role = role;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new SimpleGrantedAuthority(ROLE_PREFIX + role));
return list;
}
and user detail service
public class DefaultUserDetailService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//How to create user detail with information from httprequest?
return null;
}
}
But i only have username and role from httpRequest from 3rd party. How do i collect it?
Thanks

spring-security user UsernamePasswordAuthenticationFilter that extend AbstractAuthenticationProcessingFilter do authentication
maybe you need a new AbstractAuthenticationProcessingFilter to replace UsernamePasswordAuthenticationFilter.

Related

How do I pre-load my Spring Boot integration test with a user from my H2 database?

I'm using Spring Boot 2.1. I'm also using the H2 in memory database. I have created this file at src/test/resources/data.sql:
insert into roles (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into roles (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'ADMIN');
insert into privileges (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into privileges (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'SUPER_ADMIN');
insert into roles_privileges (role_id, privilege_id) VALUES ('1be4965cb4f311eaa2b76a0000c30600', '1be4965cb4f311eaa2b76a0000c30600');
insert into roles_privileges (role_id, privilege_id) VALUES ('1be6b2acb4f311eaa2b76a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');
insert into occasions (id, name) values ('97c625b8b63511ea9d386a0000c30600', 'Birthday');
insert into users (id, email, enabled, first_name, last_name, password, token_expired) values ('aa7625b8b63512929d386a0000c30600', 'me#example.com', true, 'lone', 'ranger', 'password', false);
insert into users_roles (user_id, role_id) values ('aa7625b8b63512929d386a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');
I would like to create a spring boot integration test to test the following method ...
#RestController
#RequestMapping("/api/cards")
public class CardController {
#Autowired
private CardService cardService;
#PostMapping
#ResponseStatus(code = HttpStatus.CREATED)
public void create(#RequestBody Card card, #AuthenticationPrincipal User loggedInUser) {
card.setAuthor(loggedInUser);
cardService.save(card);
}
and I would like to load the one user in my database, but I'm not quite sure the easiest way to do that. I tried "#WithMockUser" but that isn't loading this user ...
#SpringBootTest(classes = CardmaniaApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class CardControllerIntegrationTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private ICardRepository cardRepository;
#Autowired
private IUserRepository userRepository;
#Autowired
private IOccasionRepository occasionRepository;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Value("${jwt.http.request.header}")
private String tokenHeader;
#BeforeEach
void setup() {
UserDetails user = (UserDetails) userRepository.findAll().get(0);
final String token = jwtTokenUtil.generateToken(user);
restTemplate.getRestTemplate().setInterceptors(
Collections.singletonList((request, body, execution) -> {
request.getHeaders()
.add(this.tokenHeader, "Bearer " + token);
return execution.execute(request, body);
}));
}
#Test
#WithMockUser(authorities = {"ADMIN"})
void createCard() throws Exception {
final Card card = new Card();
final User author = userRepository.findAll().get(0);
card.setAuthor(author);
final Occasion occasion = occasionRepository.findAll().get(0);
card.setOccasion(occasion);
byte[] image = new byte[] {1, 1, 1, 1};
card.setImage(image);
ResponseEntity<String> responseEntity = this.restTemplate
.postForEntity("http://localhost:" + port + "/api/cards", card, String.class);
assertEquals(201, responseEntity.getStatusCodeValue());
List<Card> cards = cardRepository.findByOccasion(occasion);
assertThat(cards.size()).isEqualTo(1);
final Card cardEntity = cards.get(0);
assertThat(cardEntity.getImage()).isEqualTo(image);
}
}
I'm fairly new to spring boot and so unfamiliar with the simplest way to pre-load a user into my security principal.
I discovered the answer is to use the "#WithUserDetails" annotation
#Test
#WithUserDetails("me#example.com")
void registrationWorksThroughAllLayers() throws Exception {
This will use your autowired class that implements UserDetailsService and invoke its loadUserByUsername with the annotated value, "me#example.com," in the code above.
This setup is currently conflicting. One the one side you provide a mocked user for the SecurityContext using #WithMockUser and on the other side, you prepare a valid JWT token for actual authentication.
#WithMockUser is usually used in tests to e.g. easily access protected endpoints without creating a request with correct authentication information (like JWT or basic auth).
So you should pick one of the current approaches: Either go for mocking the user OR generate the JWT and access your endpoint.
The reason your #WithMockUser is not working currently, might be related to the default username Spring Security picks. If you want this to match your user in your database, consider configuring it: #WithMockUser(username="YOUR_USERNAME",roles={"USER","ADMIN"})
To debug your application further, you can temporarily add
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
to the endpoint you are testing and debug it to understand which user is now actually part of the SecurityContext.

Spring Security Authentication using DynamoDB

Does anyone know how to provide spring security authentication using DynamoDB? In other words, how would you convert/represent the following configAuthentication method using DynamoDB?
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, enabled from appuser where username = ?")
.authoritiesByUsernameQuery("select username, rolename from appuser natural join user_role natural join role where username = ?;");
}
You can use anything as a Spring Security authentication backend. You would need to write custom query logic in a class that implements the UserDetailsService interface and return a domain object that implements the UserDetails interface, but you can use that class.
Your configuration would like something like
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myCustomDynamoDbDetailService);
}
Where myCustomDynamoDBDetailService is your class that implements UserDetailService and does the lookup by username from DynamoDB.

validate access token with user name in Oauth2

I'm trying to implement OAuth2 authentication for my SPA application. am facing one issue, For example there is two users, user1 and user2 both are logged in . when the user1 trying to use user2 access token, user1 can access . i don't know how to secure access token.
Each time when a user logged in using oauth2 authentication, it associates the user-id of the newly logged in user with an unique access_token, you can get that user_id, and in turn map it with the user_id of current logged in user to find out if its the user is using his own access_token.
I have find out the solution.
To customizing the access token we can secure the users access token.
i have customize the access token using Token enhancer.
Code will be like below
#Primary
#Bean
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setTokenEnhancer(tokenEnhancer());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
and custom enhancer will be like
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
AppUserDetails appUserDetails = (AppUserDetails) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
String encodedBytes = Base64.getEncoder().encodeToString(appUserDetails.getIdUser().toString().getBytes());
additionalInfo.put("authorities", appUserDetails.getAuthorities());
additionalInfo.put("username", appUserDetails.getUsername());
additionalInfo.put("idUser", encodedBytes);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
in other method i have decode the customize access token.
Also i have used AuthorizationServerEndpointsConfigurer
#Override
//Configure the properties and enhanced functionality of the Authorization Server endpoints.
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancer()).authenticationManager(authenticationManager);
}

rest with Spring boot + spring security with custom users table

I want to configure Spring boot and spring security for some rest services.
I have tables User, priviledge, priviledge_user
I have this Configuration files
WebSecurityConfig
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().
httpBasic().and().
csrf().disable();
}
}
and AuthenticationConfiguration
#Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserRepository userRepository;
Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User account = userRepository.findByUsername(username);
log.info("The user ({}) logged has the password {}", username, account.getPassword());
org.springframework.security.core.userdetails.User userSession = new org.springframework.security.core.userdetails.User(account.getUsername(), account.getPassword(), true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
return userSession;
}
};
}
}
debuging the code seems Authentication is working well cause I have the user from database(I am using JNDI datasoure and have this configured in application.properties)
but When I try to get anything on http://localhost:8080/ and set my credentials I cant access and always get the prompt for credentials
Whats I am doing bad?
You are not instantiating the UserDetails object with the correct authorities.
Instead of always passing "USER" to AuthorityUtils.createAuthorityList method, you should pass a List with all the roles/privileges in the privilege_user table for the user you are loading. This roles by default are in the form of "ROLE_USER", "ROLE_ADMIN"...
So loading the User from the database successfully doesn't mean the authentication process is working well. You still have to tell him how to load the authorities for that User.
The problem is, you are using the DB audit with either of #CreatedBy or #LastModifiedBy annotation and yet you are trying to insert a document before any user is logged in. This use to happen, when you create a new user or create operations in a landing page controller. To overcome this, have a system user in your DB, say, system#yourdomain.com then set principal in the corresponding controller if the input params are right.
While doing so, the DBAuditor will get your system user id for the annotated filed. Make sure, you load newly created user principal after this operation.

Security Config - configureGlobal

Playing with Spring Session REST example, I'm curious is it possible to make the following flow:
1. In first time, an user passes its credentials, usename and password.
2. Spring Session will generate a token, puts it into Redis
3. Next time a user asks for some resources it passes its tiken.
My problem is that the following code is hardcoded:
code
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
code`
How can I do it to be dynamic?
Thanks in advance.
Spring Session works independent of the security framework you choose, so the answer depends on which security framework you want to use. This means selecting the username / password for your users is totally independent of Spring Session.
Since the question is unrelated to Spring Session, you should consult the documentation of the security framework you choose. In this instance, it is Spring Security so you can consult Spring Security's documentation on authentication.
The first step is to determine how you want to authenticate.
For example, if you want to use JDBC authentication, you can use something like the following.
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
By default jdbcAuthentication expects that:
select username, password, enabled from users where username = ?
will return the username, the password, and if that user is enabled. You can customize this query if you like using properties on jdbcAuthentication(). See the javadoc for details.
NOTE: It is important to understand that withDefaultSchema and withUser are really only intended for in memory databases since it will try to add the schema and the user every time. In a production environment your users and schema should be added using other mechanisms (i.e. liquibase).
The most flexible option is to implement a UserDetailsService. You can then look up users in any way you want. For example:
#Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
#Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
MyUser user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
List<GrantedAuthority> authorities = convert(user.getRoles());
return new User(user.getUsername(), user.getPassword(), authorities);
}
}
Then you can configure it using:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService);
}
You can find a complete example of using a UserDetailsService in my Spring Security 4 talk.

Resources