I'm using Oauth2.0 in spring cloud, my authorization server, im storing the token in the database using jdbcTokenStore instead of JwtTokenStore and its working properly, but when i make a http request, postman returns this error.
{
"error": "invalid_token",
"error_description": "Cannot convert access token to JSON"
}
so it's because I havent implement the method accesstokenconverter
I've been looking for how to implement this method but i havent been lucky yet.
can you help me guys
package com.restaurants.serviciooauth.security;
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.bcrypt.BCryptPasswordEncoder;
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.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import javax.sql.DataSource;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
AuthenticationManager authenticationManager;
//configuration to jdbc
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("frontendapp")
.secret(passwordEncoder.encode("12345"))
.scopes("read","write")
.authorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(3600)
.and()
.withClient("androidapp")
.secret(passwordEncoder.encode("12345"))
.scopes("read","write")
.authorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(3600);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore());
}
}
Related
This is my controller class code which I am using for getting user and save that particular user in database but after hitting data/or sending the data it was committed to my database but after that I am getting this error message.
package com.app.Exam.USerController;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.app.Exam.Models.Roles;
import com.app.Exam.Models.User;
import com.app.Exam.Models.UserRoll;
import com.app.Exam.Service.UserService;
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#PostMapping("/")
public User createUser(#RequestBody User user) throws Exception {
User user2=new User();
user2.setPassword(this.bCryptPasswordEncoder.encode(user.getPassword()));
user2.setEmail(user.getEmail());
user2.setFirstName(user.getFirstName());
user2.setLastName(user.getLastName());
user2.setPhone(user.getPhone());
user2.setUserName(user.getUserName());
Roles roll=new Roles();
//roll.setRollId(46L);
roll.setRollName("NORMAL");
//user.setUserRolls(list);
List<UserRoll> list=new ArrayList<>();
UserRoll userRoll=new UserRoll();
userRoll.setRoles(roll);
userRoll.setUser(user2);
list.add(userRoll);
roll.setUserRolls(list);
User local=this.userService.CreateUser(user2,list);
return local;
}
#GetMapping("/{username}")
private User getUserByName(#PathVariable("username") String username) {
User user=this.userService.getUserByName(username);
return user;
}
// #GetMapping("/{id}")
// private User deleteUser(#PathVariable("id") Long id) throws Exception {
// User user=this.userService.deleteUserbyid(id);
// return user;
// }
}
My spring security config class was as below:
package com.app.Exam.Security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.app.Exam.JwtConfig.JwtAuthenticationEntryPoint;
import com.app.Exam.JwtConfig.JwtRequestFilter;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
JwtRequestFilter jwtAuthenticationFilter;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.csrf().disable().cors().disable()
.authorizeHttpRequests()
.requestMatchers("/Authenticate","/user/").permitAll()
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authenticationProvider(this.daoAuthenticationProvider());
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
// #Override
// protected void configure(HttpSecurity http) throws Exception {
// http
// .csrf().disable().cors()
// .disable().authorizeHttpRequests().antMatchers("/Authenticate","/user/").permitAll()
// .antMatchers(HttpMethod.OPTIONS).permitAll()
// .anyRequest().authenticated()
// .and()
// .exceptionHandling()
// .authenticationEntryPoint(jwtAuthenticationEntryPoint)
// .and()
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//
// http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
// }
//
#Bean
PasswordEncoder pass() {
return new BCryptPasswordEncoder();
}
// #Bean
// AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
// return authenticationConfiguration.getAuthenticationManager();
// }
#Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider=new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(this.pass());
return daoAuthenticationProvider;
}
}
My errors Stackstrace is as belows..
enter image description here
enter image description hereenter code here
enter image description here
After to many reaserch i found my answer this error happens because i am not providing the user roll from front end and defining it harrcoded in my backend
if we wanted to do that we have to specify that property in model with #JsonIgnore like bellow one
#OneToMany(cascade = CascadeType.ALL ,fetch = FetchType.EAGER,mappedBy ="user")
#JsonIgnore
private List userRolls=new ArrayList<>();
it informs the serverlet request to egnore the null json of that specific property......
For this kind of databind errors use #JsonIgnore it works for me. this is jaxb databind error thats why spring security throwing Access denied error After Commiting data to Db...
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
I am using Spring Security with Spring Boot and authenticate my users via JASIG CAS. Some of the pages require explicitly authentication (.authenticated()) and some of them are for all users.
Now there is a certain area in the menu, which indicates the current user and possible actions, like login/logout.
My main problem is now that the main page is public (permitAll()) and that, if a user already has a CAS session through some other application, he is shown as "anonymousUser" until login is clicked manually or a protected page is opened.
Is there somebody who has any ideas on how to get this working?
My security configuration:
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private CasAuthenticationProvider authProvider;
#Bean
public ServiceProperties serviceProperties() {
ServiceProperties sp = new ServiceProperties();
sp.setSendRenew(false);
sp.setService(env.getProperty("app.url") + "/j_spring_cas_security_check");
return sp;
}
#SuppressWarnings("rawtypes")
#Autowired
private AuthenticationUserDetailsService customUserDetailsService() {
return new CASUserDetailsService();
}
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService());
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("an_id_for_this_auth_provider_only");
return casAuthenticationProvider;
}
#Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(env.getProperty("cas.service.url"));
}
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setAuthenticationSuccessHandler(savedRequestAwareAuthenticationSuccessHandler());
return casAuthenticationFilter;
}
#Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setLoginUrl(env.getProperty("cas.service.url") + "/login");
ep.setServiceProperties(serviceProperties());
return ep;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**").antMatchers("/fonts/**").antMatchers("/images/**").antMatchers("/css/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().
authenticationEntryPoint(casAuthenticationEntryPoint()).and().addFilter(casAuthenticationFilter()).
logout().logoutUrl("/caslogout").addLogoutHandler(logoutHandler()).logoutSuccessUrl("/").deleteCookies("JSESSIONID").permitAll().and().
csrf().disable().headers().frameOptions().disable().authorizeRequests().antMatchers("/rest/**").permitAll().
antMatchers("/login/**").authenticated().antMatchers("/settings/**").authenticated().
antMatchers("/projects/*/settings").authenticated().antMatchers("/projects/*/role").authenticated().
antMatchers("/projects/*/*/admin").authenticated().antMatchers("/**").permitAll();
}
#Bean
public SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler() {
CASAuthSuccessHandler auth = new CASAuthSuccessHandler();
return auth;
}
#Bean
public CASLogoutHandler logoutHandler() {
CASLogoutHandler logout = new CASLogoutHandler();
return logout;
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
}
}
What you are looking for is CAS's Gateway feature. Currently this is not supported in Spring Security. There is a JIRA logged to support it and a Pull Request that is waiting additional modifications based on my feedback to the submitter.
I'd take a look at the Pull Request as it demonstrates a few options on how to implement this. Please do read through the whole thing as you will need to make some changes to the Pull Request to ensure your application performs.