I am very new to Spring Security but I have 2 endpoints: /login and /welcome
When I call the endpoint /login, it tells me that I am logged in. My goal is that I stay logged in. Since for now, I get a 401 status when I call /welcome after I logged in.
I know that I could use setAuthentication() in the welcome() method but I don't want to do this for each controller. So how can I set the authentication only once after the login?
package com.example.spring_security.controller;
import com.example.spring_security.config.CustomUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
#RestController
#RequestMapping("/")
public class HelloController {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserDetailsManager userDetailsManager;
#GetMapping("/welcome")
public String welcome() {
return "Welcome! You are logged in.";
}
#GetMapping("/home")
public String home() {
return "Home - Please register!";
}
#PostMapping("/register")
public void add(#RequestBody com.example.spring_security.entity.User userDto) {
if (userDto.getUsername() == null || userDto.getUsername().isEmpty()) {
throw new IllegalArgumentException("Username may not be empty or null");
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UserDetails user = new CustomUserDetails(
userDto.getUsername(),
new BCryptPasswordEncoder().encode(userDto.getPassword()),
authorities,
false
);
userDetailsManager.createUser(user);
}
#PostMapping("/login")
public ResponseEntity<Object> login(#RequestBody CustomUserDetails user) {
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
if (userDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if (!passwordEncoder.matches(user.getPassword(), userDetails.getPassword())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
return ResponseEntity.ok("Login successful");
}
catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("invalid credentials");
}
}
}
package com.example.spring_security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import javax.sql.DataSource;
#Configuration
public class SecurityConfig {
private final DataSource dataSource;
public SecurityConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionAuthenticationStrategy(sessionAuthenticationStrategy());
http.authorizeHttpRequests(auth ->
auth
.requestMatchers("/home", "/register", "/login").permitAll()
.requestMatchers("/welcome").hasRole("ROLE_USER")
.anyRequest().authenticated()
);
http.httpBasic();
http.logout().permitAll();
return http.build();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
public UserDetailsService userDetailsService() {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
manager.setDataSource(dataSource);
return manager;
}
#Bean
public PasswordEncoder passwordEncoder() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("password"));
return new BCryptPasswordEncoder();
}
#Bean
public UserDetailsManager userDetailsManager() {
JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager();
userDetailsManager.setDataSource(dataSource);
return userDetailsManager;
}
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new SessionFixationProtectionStrategy();
}
}
Related
I am workin on spring application with neo4j. I'd like to add authentication using username and password from the database. Below I put the code on how I'd do it using mysql. I'm wondering whats the equivalent for the code below using neo4j.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Bean
public PasswordEncoder passwordEncoders(){
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.usersByUsernameQuery("SELECT u.name, u.password, 1 FROM user u WHERE u.name=?")
.authoritiesByUsernameQuery("SELECT u.name, u.role, 1 FROM user u WHERE u.name=?")
.dataSource(dataSource)
.passwordEncoder(passwordEncoders());
}
//...
}
I tried creating Beans for DataSource but I get BeanCreationException. Here is what I tried to use
#Bean
public DataSource getDataSource(){
String NEO4J_URL = System.getenv("NEO4J_URL");
if (NEO4J_URL==null) NEO4J_URL=System.getProperty("NEO4J_URL","jdbc:neo4j:http://localhost:11010");
return new DriverManagerDataSource(NEO4J_URL);
}
or this
#Bean
public DataSource getDataSource(){
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.neo4j.driver");
dataSourceBuilder.url("bolt://localhost:11010");
dataSourceBuilder.username("neo4j");
dataSourceBuilder.password("0000");
return dataSourceBuilder.build();
}
You will need to register a custom Security AuthenticationProvider that can query the graph to retrieve the users with the given credentials.
Such a provider would look like this :
package com.ikwattro.demo.neo4jauth.security;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Session;
import org.neo4j.driver.types.Node;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
#Component
public class Neo4jAuthenticationProvider implements AuthenticationProvider {
private final Driver driver;
public Neo4jAuthenticationProvider(Driver driver) {
this.driver = driver;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
try (Session session = driver.session()) {
List<Record> results = session.run("MATCH (n:User) WHERE n.username = $name AND n.password = $password RETURN n",
Map.of("name", name, "password", password)).list();
if (results.isEmpty()) {
return null;
}
Node user = results.get(0).get("n").asNode();
// Possible to add more information from user
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
final UserDetails principal = new User(name, password, authorities);
return new UsernamePasswordAuthenticationToken(principal, password, authorities);
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
You then need to register this provider as your main AuthenticationProvider in the Security chain, you do this in the Security Configuration :
package com.ikwattro.demo.neo4jauth.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Neo4jAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
You can then query your application with basic authentication
curl --user john:doe localhost:8080/hello
You can find a fully working demo here : https://github.com/ikwattro/spring-security-neo4j
I have implemented below code for spring security.
I am using spring version 2.3.0.BUILD-SNAPSHOT with security oauth-2 version 2.3.0.RELEASE
Is It possible to remove / revoke granted access token once its expiry time is completed? If so which class/method should be place and where, to achieve the goal ?
AuthenticationService.java
package com.oauth.config;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.oauth.model.UserInfo;
import com.oauth.service.UserService;
#Service("authenticationService")
public class AuthenticationService implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest request = (
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
System.out.println("userId: " + request.getParameter("userId"));
UserInfo user = null;
try {
user = userService.getUserInfoByUserName(username);
GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole());
return new User(user.getUserName(), user.getPassword(), Arrays.asList(authority));
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException("Invalid user id or password.");
}
}
}
Outh2Configuration.java
package com.oauth.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
/*
* #Autowired private CustomLogoutSuccessHandler customLogoutSuccessHandler;
*/
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//.and().logout().logoutUrl("/oauth/logout").logoutSuccessHandler(customLogoutSuccessHandler)
.and().authorizeRequests()
.antMatchers("/noAuth/**","/customerNoAuth/**" ,"/oauth/token").permitAll()
.anyRequest().denyAll()
.and().csrf().disable()
.exceptionHandling();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String PROP_CLIENTID = "client";
private static final String PROP_SECRET = "secret";
private static final int PROP_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 7;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("myAuthenticationManager")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
//.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.tokenServices(tokenServices());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String secretEncoded = passwordEncoder().encode(PROP_SECRET);
clients
.inMemory()
.withClient(PROP_CLIENTID)
.scopes("read", "write", "trust")
.authorities("ADMIN", "USER")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.secret(secretEncoded)
.accessTokenValiditySeconds(PROP_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(PROP_TOKEN_VALIDITY_SECONDS);
}
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void setEnvironment(Environment arg0) {
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
CustomTokenService defaultTokenServices = new CustomTokenService();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
}
WebSecurityConfiguration.java
package com.oauth.config;
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationService authenticateService;
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/noAuth/**",
"/customerNoAuth/**",
"/state/**",
"/country/**",
"/violation/**",
"/ticketViolation/**",
"/api/zipcode/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(authenticateService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
CustomTokenService.java
package com.oauth.config;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.transaction.annotation.Transactional;
import lombok.Synchronized;
public class CustomTokenService extends DefaultTokenServices {
private final Logger log = LogManager.getFormatterLogger(getClass());
#Override
#Transactional
#Synchronized
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
try {
int count = 0;
OAuth2AccessToken accessToken = null;
try{
accessToken = super.createAccessToken(authentication);
}catch(DuplicateKeyException e){
do{
log.info("===========DuplicateKeyException===========");
log.info(String.format("Duplicate user found for %s",authentication.getUserAuthentication().getPrincipal()));
Thread.sleep(500);
try {
return accessToken = super.createAccessToken(authentication);
} catch (DuplicateKeyException e2) {
++count;
log.error("Attempt count " + count);
}
}while(count<=5);
}
return accessToken;
}catch (Exception ex) {
log.info(String.format("Exception while creating access token %s",ex));
}
return null;
}
}
CustomLogoutSuccessHandler.java
package com.oauth.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
/**
* Spring Security logout handler
*/
//#Component
public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
private static final String BEARER_AUTHENTICATION = "bearer ";
private static final String HEADER_AUTHORIZATION = "authorization";
#Autowired
private TokenStore tokenStore;
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String token = request.getHeader(HEADER_AUTHORIZATION);
if (token != null && token.startsWith(BEARER_AUTHENTICATION)) {
String accessToken = token.substring(token.indexOf(" ") + 1);
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
if (oAuth2AccessToken != null) {
tokenStore.removeAccessToken(oAuth2AccessToken);
}
}
response.setStatus(HttpServletResponse.SC_OK);
}
}
You should not store tokens in ur DB.If anyone can access ur db ,he can easily get access to every data.Again if u want to store then i think u can make a scheduler to delete token.
I have a project using spring boot, spring security with oauth2. When I use
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this method returns only username end in the examples that I see this method returns UserDetails implentation.
Follow the settings
OAuthSecurityConfig.java:
package br.com.altha.api.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import br.com.altha.api.security.CustomUserDetailsService;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#EnableAuthorizationServer
#EnableResourceServer
#Order(SecurityProperties.BASIC_AUTH_ORDER-2)
#Import(Encoders.class)
public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder userPasswordEncoder;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(userPasswordEncoder);
}
}
AuthorizationServerConfig.java:
package br.com.altha.api.config;
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.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import br.com.altha.api.security.CustomUserDetailsService;
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String SECRET = "PASSWORD";
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private PasswordEncoder oauthClientPasswordEncoder;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("altha-adms")
.secret(oauthClientPasswordEncoder.encode(SECRET))
.scopes("write", "read")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(60/*1800*/)
.refreshTokenValiditySeconds(1800);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager)
.reuseRefreshTokens(false)
.userDetailsService(userDetailsService);
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SECRET);
return converter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
ResourceServerConfig.java:
package br.com.altha.api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
import br.com.altha.api.handler.RestExceptionHandler;
#Configuration
#Import(Encoders.class)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Bean
public RestExceptionHandler handlerError() {
return new RestExceptionHandler();
}
#Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/private/**").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.stateless(true);
}
}
I was able to solve this problem with this code:
I added a bean to UserAuthenticationConverter
#Bean
public UserAuthenticationConverter userAuthenticationConverter() {
DefaultUserAuthenticationConverter defaultUserAuthenticationConverter = new DefaultUserAuthenticationConverter();
defaultUserAuthenticationConverter.setUserDetailsService(userDetailsService);
return defaultUserAuthenticationConverter;
}
After this, I set this bean in the JwtAccessTokenConverter
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SECRET);
((DefaultAccessTokenConverter) jwtAccessTokenConverter.getAccessTokenConverter())
.setUserTokenConverter(userAuthenticationConverter());
return jwtAccessTokenConverter;
}
The cause would vary based on authentication/authorization technique but in my case I have 2 filters authentication/authorization the issue was passing the username instead of the whole user object to the UsernamePasswordAuthenticationToken in the authorization filter :
return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), .....);
The first constructor argument is being set to Principle object.
So fix was to pass the whole userDetails object
return new UsernamePasswordAuthenticationToken(userDetails, .....);
With Spring Boot 1.5.x, you can implement a PrincipalExtractor and override it's Object extractPrincipal(Map<String, Object> map). The following example component has a UserdetailsService autowired in to lookup the UserDetails object based on the username.
#Component
public class MyPrincipalExtractor implements PrincipalExtractor {
private UserDetailsService userDetailsService;
#Value("${security.oauth2.client.principal-attribute}")
private String principalAttribute;
#Autowired
public MyPrincipalExtractor(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public Object extractPrincipal(Map<String, Object> map) {
if (!map.containsKey(principalAttribute)) {
return null;
}
final String username = (String) map.get(principalAttribute);
try {
return userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException e) {
// This may be the first time this user is accessing the system,
// maybe you want to extract some other attributes from the map
// and return a different type of user object that can be used to
// create a new user.
}
}
}
Now the SecurityContextHolder.getContext().getAuthentication().getPrincipal() will contain a UserDetails object.
For a more detailed tutorial see:
https://www.baeldung.com/spring-security-oauth-principal-authorities-extractor
https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/api/org/springframework/boot/autoconfigure/security/oauth2/resource/PrincipalExtractor.html
Please help to exclude additional info from response object and add only to JWT.
I add tokenEnhancer to add additional info to JWT, but it appears in responce object too.
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyLXJlc291cmNlIl0sImZ1bGxfbmFtZSI6ImhleWJhdCBndWxpeWV2IiwidXNlcl9uYW1lIjoiaGV5YmF0Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIiwidHJ1c3QiXSwiZXhwIjoxNTQ5NTQxNTkwLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiY2Y5ZmY0ZDMtOGZiYi00NWFlLWJhMzEtYWM0MjUzNGFjNDI2IiwiY2xpZW50X2lkIjoibXktdHJ1c3RlZC1jbGllbnQifQ.QLVoRxv0kpTx-ruTR55ldMcQzl8M1KGjuWVeNYlH5Ly35vvdA3PvZelYZVpGq0BDr4fHyiUKy-MLP1H0X8_qiT_2onMHTtzvVDa6Wjrx4Ri3q0wOvXQlyFGenz73kxcHkK-YQ8Y8FYKnq3NUBhz-FBFf9JaQOcmsNrrNiF64SlpU3fO2uqbbP6_1_bQ9QHhOHMXxtk0Scz7-gFZm9Ln92K10wd4jLvp1yvSU9X1Hh9lBafB-WAHgVt5eoaZzrbZ7YJYUqNBifoW77NMisNdWIDIqL90jeKBGN6GVm7QRJOo9wNV6tsOLP9-fRgbHu_teqOQe5v_gK3f52xr4jBYs2w",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyLXJlc291cmNlIl0sImZ1bGxfbmFtZSI6ImhleWJhdCBndWxpeWV2IiwidXNlcl9uYW1lIjoiaGV5YmF0Iiwic2NvcGUiOlsicmVhZCIsIndyaXRlIiwidHJ1c3QiXSwiYXRpIjoiY2Y5ZmY0ZDMtOGZiYi00NWFlLWJhMzEtYWM0MjUzNGFjNDI2IiwiZXhwIjoxNTQ5NjI3ODEwLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMjRkYzliNDEtMTdiMC00ZGQ2LWI3ZTgtODljMWNiNWRmMWQ2IiwiY2xpZW50X2lkIjoibXktdHJ1c3RlZC1jbGllbnQifQ.cIi1yfyNt-komhvduQFPAC098jeEeeqHbiU6HFDNk5uNuvR5HwYkjpKkowvRaLfpXTPGHgpmwNSr3I8LolESLzTfjtPRmFsbrRaLavib5_h32-EpJi5RBKbjhtItxGYbku92K5AxSH9dJ8ceRYg3VQi6zSKrodXXekIdrgtPaGvmEuMcEZF7Oh2qekzvo5u8_1ShZptc76dmAWADMrbHgFxggxioBBOGI27YWvnIPFVLV-RMxrh6FjM_Cy7Tr5BF_13klJvlF2EapkgDQMx0KL53cWvzriZOiDhLcJ6jJk2Cm7oMI5qobcHx7sGD5OC49UYn9LSHQzqh6RqRhckI5g",
"expires_in": 179,
"scope": "read write trust",
"full_name": "heybat guliyev",
"jti": "cf9ff4d3-8fbb-45ae-ba31-ac42534ac426" }
as you see full name appears in response object too
package com.hqsoft.shop.config;
import com.hqsoft.shop.entities.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap;
import java.util.Map;
/****** to add additional info in token structure ******/
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> map = new HashMap<>();
map.put("full_name", user.getFullName());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
System.out.println(accessToken);
return accessToken;
}
}
package com.hqsoft.shop.config;
import com.hqsoft.shop.service.ClientService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.util.Arrays;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
private PasswordEncoder oauthClientPasswordEncoder;
private AuthenticationManager authenticationManager;
private ClientService clientService;
public AuthorizationServer(PasswordEncoder oauthClientPasswordEncoder,
AuthenticationManager authenticationManager,
ClientService clientService
) {
this.oauthClientPasswordEncoder = oauthClientPasswordEncoder;
this.authenticationManager = authenticationManager;
this.clientService = clientService;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
}
/****** for configuring client ******/
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.tokenEnhancer(tokenEnhancerChain);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
#Primary
public DefaultTokenServices defaultTokenService() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/****** jwt token implementation ******/
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("keys.jks");
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, "*******".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey"));
return jwtAccessTokenConverter;
}
}
Override the method convertUserAuthentication of DefaultUserAuthenticationConverter:
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
#Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = super.convertUserAuthentication(authentication);
// Add additional info
User user = (User) authentication.getPrincipal();
response.put("full_name", user.getFullName());
return response;
}
}
In your configuration create a new token converter and set its user token converter to your new CustomUserAuthenticationConverter, then use this token converter in your jwtAccessTokenConverter:
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("keys.jks");
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, "*******".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey"));
// ...
AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new CustomUserAuthenticationConverter());
jwtAccessTokenConverter.setAccessTokenConverter(customAccessTokenConverter);
// ...
return jwtAccessTokenConverter;
}
Finally, only use this token converter instead of the token enhancer chain:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.tokenConverter(jwtAccessTokenConverter());
}
I want to make an authentication with password encoding.
When I use PasswordEncoder() and setPasswordEncryptor(), it gives me errors:
PasswordEncoder() : Cannot instantiate the type PasswordEncoder and for setPasswordEncryptor() : The method setPasswordEncryptor(StrongPasswordEncryptor) is undefined for the type PasswordEncoder.
I don't know what I should I do and I search for it too much.
thank you guys!
import org.jasypt.util.password.StrongPasswordEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("daoAuthenticationProvider")
public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder(StrongPasswordEncryptor passwordEncryptor) {
PasswordEncoder passwordEncoder = new PasswordEncoder();
passwordEncoder.setPasswordEncryptor(passwordEncryptor);
return passwordEncoder;
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder,
UserDetailsService userDetailsService) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
#Autowired
public void configureAuthManager(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests().antMatchers("/", "/products", "/product/show/*", "/console/*", "/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
httpSecurity.csrf().disable();
httpSecurity.headers().frameOptions().disable();
}
}
Try the following code snippet. It should work.
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Bean
public DaoAuthenticationProvider authProvider(UserDetailsService userDetailsService) {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
Use this : Spring Boot 2
#Bean(name = "passwordEncoder")
#Qualifier("passwordEncoder")
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}