Keycloak + Spring Boot + Swagger 2 - spring-boot

I am trying to authenticate the user before he accesses the swagger-ui. I am using Keycloak for ID management. I am following the example given here.
Below is my security config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = new KeycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
authenticationManagerBuilder.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().accessDeniedHandler(
(request, response, accessDeniedException) -> response.setStatus(HttpServletResponse.SC_NOT_FOUND)
)
.and()
.anonymous()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic().disable()
.formLogin().disable()
.logout().disable();
}
}
Below is my swagger config
#Configuration
#Slf4j
public class SwaggerConfig {
#Value("${keycloak.auth-server-url}")
private String AUTH_SERVER;
#Value("${keycloak.credentials.secret}")
private String CLIENT_SECRET;
#Value("${keycloak.resource}")
private String CLIENT_ID;
#Value("${keycloak.realm}")
private String REALM;
private static final String OAUTH_NAME = "spring_oauth";
private static final String ALLOWED_PATHS = "src/main/java/io/chait/swagger/demo/.*";
private static final String GROUP_NAME = "swagger-demo";
private static final String TITLE = "API Documentation for swagger-demo Application";
private static final String DESCRIPTION = "Description here";
private static final String VERSION = "1.0";
#Bean
public Docket taskApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(GROUP_NAME)
.useDefaultResponseMessages(true)
.apiInfo(apiInfo())
.select()
.paths(regex(ALLOWED_PATHS))
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()));
}
private ApiInfo apiInfo() {
return new
ApiInfoBuilder().title(TITLE).description(DESCRIPTION).version(VERSION).build();
}
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.realm(REALM)
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.appName(GROUP_NAME)
.scopeSeparator(" ")
.build();
}
private SecurityScheme securityScheme() {
GrantType grantType =
new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/realms/" + REALM + "/protocol/openid-connect/token", GROUP_NAME))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/realms/" + REALM + "/protocol/openid-connect/auth", CLIENT_ID, CLIENT_SECRET))
.build();
SecurityScheme oauth =
new OAuthBuilder()
.name(OAUTH_NAME)
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("user", "for CRUD operations"),
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations")
};
return scopes;
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(Arrays.asList(new SecurityReference(OAUTH_NAME, scopes())))
.forPaths(PathSelectors.regex(ALLOWED_PATHS))
.build();
}
}
Below is my properties file
spring.datasource.url=jdbc:postgresql://localhost:5432/personal
spring.datasource.password=postgres
spring.datasource.username=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
# keycloak config
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.realm=local
keycloak.resource=swagger-demo
keycloak.public-client=true
keycloak.credentials.secret=fb652c4a-70cb-4b81-a339-fa87054f77a0
spring.application.name=swagger-demo
Below is my boot class
#SpringBootApplication
#EnableJpaRepositories
#EnableSwagger2
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
When I fire up the application and access http://localhost:8080/swagger-demo/swagger-ui/ the app redirects me to Keycloak where I enter user credentials. Upon authenticating, I get the exception below
2021-04-02 21:46:04.163 ERROR 4208 --- [nio-8080-exec-8] o.k.adapters.OAuthRequestAuthenticator : failed to turn code into token
2021-04-02 21:46:04.164 ERROR 4208 --- [nio-8080-exec-8] o.k.adapters.OAuthRequestAuthenticator : status from server: 401
2021-04-02 21:46:04.164 ERROR 4208 --- [nio-8080-exec-8] o.k.adapters.OAuthRequestAuthenticator : {"error":"unauthorized_client","error_description":"Client secret not provided in request"}
All the related threads regarding this exception are pointing towards a change in nginx config or Docker config. I am not using either of them and I am running this entirely on local. I am not sure what I am missing here. I have the code here. Any help is appreciated.

Related

Cannot redirect to a URL was defiend before in spring boot project with Oauth2 Login google

Hi i am new member of stackoverflow, i had a problem with my springboot project.Im trying to redirect after login google is successful, but its always show "There was an unexpected error (type=Not Found, status=404).
This is my configuration:
#Configuration
#EnableWebSecurity
#Order(2)
#PropertySource("classpath:application.properties")
public class ConfigSecuriry extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/login/**",
"/login/oauth2/code/google",
"/login/google",
"/api/token/refresh",
"**/swagger-resources/**",
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**",
"/swagger-resources",
"/swagger-resources/configuration/ui",
"/swagger-resources/configuration/security"
).permitAll();
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/login/google")
.defaultSuccessUrl("/abc")
.successHandler(savedRequestAwareAuthenticationSuccessHandler())
.and()
.oauth2Client();
}
private SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler() {
SavedRequestAwareAuthenticationSuccessHandler successHandler
= new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setDefaultTargetUrl("/abc"); // The URL to redirect to after a successful login
successHandler.setAlwaysUseDefaultTargetUrl(true);
return successHandler;
}
#Bean
public static ClientRegistrationRepository clientRegistrationRepository() {
ClientRegistration registration = ClientRegistration.withRegistrationId("google")
.clientId("XXXXXXXX")
.clientSecret("XXXXXXX")
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
.scope("openid", "profile", "email")
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
.tokenUri("https://www.googleapis.com/oauth2/v4/token")
.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
.userNameAttributeName(IdTokenClaimNames.SUB)
.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
.clientName("Google")
.build();
return new InMemoryClientRegistrationRepository(registration);
}
}
and this is my controller:
#RestController
public class LoginGoogle {
#GetMapping("/abc")
public String sucess(){
return "Succes login google";
}
private final OAuth2AuthorizedClientService authorizedClientService;
public LoginGoogle(OAuth2AuthorizedClientService authorizedClientService) {
this.authorizedClientService = authorizedClientService;
}
#GetMapping("/login/google")
public RedirectView googleLogin(HttpServletRequest request) throws Exception {
String redirectUri = UriComponentsBuilder.fromHttpUrl(getBaseUrl(request))
.path("/login/oauth2/code/google")
.toUriString();
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(),
new JacksonFactory(),
"XXXXXXXX",
"XXXXXXXX",
Arrays.asList("email", "profile"))
.setAccessType("offline")
.build();
String url = flow.newAuthorizationUrl()
.setRedirectUri(redirectUri)
.setAccessType("offline")
.setApprovalPrompt("force")
.build();
return new RedirectView(url);
}
private String getBaseUrl(HttpServletRequest request) {
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
}
}
i was try anything i found but it doesn't seem to work.I look forward to receiving your contributions,thank you for all your assistance

Error occured when Spring Security CustomLoginFilter is applied! somebody help me

I want to apply CustomLoginProcessingFilter in my application but i can't figure out how it works!
I'm using Spring boot 2.7.2, the lastest version when i started studying this.
here's my code
Another custom providers or custom detail services work so well.
But, once i enroll new bean fore login processing filter, AjaxLoginProcessingFilter, they tell me that i need to specify authentitcationManager!
so, i added at filterChain method this in SecurityConfig.java, but it doesn't work.
enter image description here
/**
----------------- SecurityConfig -------------------------
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#RequiredArgsConstructor
public class SecurityConfig {
private final CustomAuthenticationSuccessHandler authenticationSuccessHandler;
private final CustomAuthenticationFailureHandler authenticationFailureHandler;
private final FormAuthenticationDetailsSource authenticationDetailsSource;
private final AjaxLoginProcessingFilter ajaxLoginProcessingFilter;
#Bean
AuthenticationManager authenticationManager(AuthenticationManagerBuilder builder) throws Exception {
return builder.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(registry -> {
registry.antMatchers("/","/users","user/login/**","/login*").permitAll()
.antMatchers("/mypage").hasRole("USER")
.antMatchers("/messages").hasRole("MANAGER")
.antMatchers("/config").hasRole("ADMIN")
.anyRequest().authenticated();
}).formLogin(login -> {
login.loginPage("/login")
.loginProcessingUrl("/login_proc")
.defaultSuccessUrl("/")
.authenticationDetailsSource(authenticationDetailsSource)
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll();
}).exceptionHandling(exception -> {
exception.accessDeniedHandler(accessDeniedHandler());
})
.addFilterBefore(ajaxLoginProcessingFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
CustomAccessDeniedHandler accessDeniedHandler = new CustomAccessDeniedHandler();
accessDeniedHandler.setErrorPage("/denied");
return accessDeniedHandler;
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() throws Exception {
return (web) -> web.ignoring().antMatchers("/resources/**");
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
/*Explanation:
In the old version you inject AuthenticationManagerBuilder, set userDetailsService, passwordEncoder and build it.
But authenticationManager is already created in this step.
It is created the way we wanted (with userDetailsService and the passwordEncoder).
https://stackoverflow.com/questions/72381114/spring-security-upgrading-the-deprecated-websecurityconfigureradapter-in-spring
*/
#Bean
CustomUserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
#Bean
public AuthenticationProvider authenticationProvider() {
return new CustomAuthenticationProvider();
}
}
-++------------------ AjaxLoginProcessingFilter ---------------------
#Component("loginProcessingFilter")
public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
private ObjectMapper objectMapper = new ObjectMapper();
public AjaxLoginProcessingFilter() {
super(new AntPathRequestMatcher("/api/login"));
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if(isAjax(request)){
throw new IllegalStateException("Authentication is not supported");
}
AccountDto accountDto = objectMapper.readValue(request.getReader(), AccountDto.class);
if(StringUtils.isEmpty(accountDto.getUsername()) || StringUtils.isEmpty(accountDto.getPassword())){
throw new IllegalArgumentException("Username or password is not empty");
}
AjaxAuthenticationToken authenticationToken = new AjaxAuthenticationToken(accountDto.getUsername(), accountDto.getPassword());
return getAuthenticationManager().authenticate(authenticationToken);
}
private boolean isAjax(HttpServletRequest request) throws IOException {
if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){
return true;
}
return false;
}
}

Roles Hierarchy in Spring Webflux Security

I have implemented Webflux security by implementing:
ReactiveUserDetailsService
ReactiveAuthenticationManager
ServerSecurityContextRepository
Now, I am trying to introduce RoleHierarchy following the docs here: Role Hierarchy Docs
I have a user with role USER but he is getting 403 Denied on hitting a controller annotated with GUEST role. Role hierarchy is: "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST"
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final DaoAuthenticationManager reactiveAuthenticationManager;
private final SecurityContextRepository securityContextRepository;
private static final String ROLE_HIERARCHIES = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";
#Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
#Bean(name = "roleHierarchy")
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHIES);
return roleHierarchy;
}
#Bean(name = "roleVoter")
public RoleVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy());
}
}
#Component
public class DaoAuthenticationManager implements ReactiveAuthenticationManager {
private final DaoUserDetailsService userDetailsService;
private final Scheduler scheduler;
#Autowired
public DaoAuthenticationManager(DaoUserDetailsService userDetailsService,
Scheduler scheduler) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.userDetailsService = userDetailsService;
this.scheduler = scheduler;
}
#Override
public Mono<Authentication> authenticate(Authentication authentication) {
final String username = authentication.getName();
return this.userDetailsService.findByUsername(username)
.publishOn(this.scheduler)
.switchIfEmpty(
Mono.defer(() -> Mono.error(new UsernameNotFoundException("Invalid Username"))))
.map(u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(),
u.getAuthorities()));
}
}
#Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private final DaoAuthenticationManager authenticationManager;
#Autowired
public SecurityContextRepository(DaoAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
if (request.getHeaders().containsKey("userName") &&
!Objects.requireNonNull(request.getHeaders().get("userName")).isEmpty()) {
String userName = Objects.requireNonNull(swe
.getRequest()
.getHeaders()
.get("userName")).get(0);
Authentication auth = new UsernamePasswordAuthenticationToken(userName,
Security.PASSWORD);
return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
} else {
return Mono.empty();
}
}
}
Anyway to get the role hierarchy thing working in Webflux security.
EDIT
Controller:
#GetMapping
#PreAuthorize("hasRole('USER')")
public Mono<Device> getDevice(#RequestParam String uuid) {
return deviceService.getDevice(uuid);
}
Normal role authorization is working for me, whats not working is the hierarchy part.
Here a very naive solution by overriding DefaultMethodSecurityExpressionHandler.
I supposed you annotated your controller with this king of expression : #PreAuthorize("hasRole('ROLE_USER')")
securityConfig.java
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final DaoAuthenticationManager reactiveAuthenticationManager;
private final SecurityContextRepository securityContextRepository;
private static final String ROLE_HIERARCHY = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";
#Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHY);
return roleHierarchy;
}
// Overriding spring default bean
#Bean
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy);
return handler;
}
}
Then you have to authorize spring bean overriding by modifying your application property file:
application.properties
spring.main.allow-bean-definition-overriding=true
Sources : issue 1 issue role hierarchy doc
Going a little bit further... This part can be optimized and cleaner.
Using url patterns setup from ServerHttpSecurity object.
Note that the following setup won't use role hierarchy :
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").hasRole("ROLE_USER") // This won't use role hierarchy because it will use implemention of hasRole defined in your 'reactiveAuthenticationManager'
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
A solution could be to create your own implementation of ReactiveAuthorizationManager and overriding check method in order to call access(...) from your http object (ServerHttpSecurity). Ie :
public class CustomReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
private final static Logger logger = LoggerFactory.getLogger(CustomReactiveAuthorizationManager.class);
private final RoleHierarchyVoter roleHierarchyVoter;
private final String authority;
CustomReactiveAuthorizationManager(String role, RoleHierarchy roleHierarchy) {
this.authority = ROLE_PREFIX + role;
this.roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy);
}
#Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return authentication
.map(a -> {
ConfigAttribute ca = (ConfigAttribute) () -> authority;
int voteResult = roleHierarchyVoter.vote(a, object, Collections.singletonList(ca));
boolean isAuthorized = voteResult == AccessDecisionVoter.ACCESS_GRANTED;
return new AuthorizationDecision(isAuthorized);
})
.defaultIfEmpty(new AuthorizationDecision(false))
.doOnError(error -> logger.error("An error occured voting decision", error));
}
}
and then calling access method :
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, RoleHierarchy roleHierarchy() {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").access(new CustomReactiveAuthorizationManager<>("USER", roleHierarchy))
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
One way I was able to achieve role hierarchy in Webflux was by creating custom annotations.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasRole('ADMIN')")
public #interface IsAdmin {
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public #interface IsUser {
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ADMIN', 'USER', 'GUEST')")
public #interface IsGuest {
}
–––––––––––––––––
And annotating the controllers like this:
#GetMapping
#IsUser
public Mono<Device> getDevice(#RequestParam String uuid) {
return deviceService.getDevice(uuid);
}
#PostMapping
#IsAdmin
#ResponseStatus(HttpStatus.CREATED)
public Mono<Device> createDevice(#Valid #RequestBody Device device) {
return deviceService.createDevice(device);
}

springboot oauth how to validate access_token

Hello everyone hope you doing well,
i have problem using open authentication in spring boot, when accessing page rest with postman is not even using param access token it still show the result, this my code please help???
Authorization Server Config class:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler);
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// TODO Auto-generated method stub
clients.inMemory()
.withClient("admin").secret("123")
.scopes("read","write")
.authorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(5*60)
.refreshTokenValiditySeconds(10*60);
}
}
Resource Server Config
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
#Override
public void configure(HttpSecurity http)throws Exception{
http
.anonymous().disable()
.authorizeRequests().antMatchers("/api/**") /** this
.authenticated()
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Security Config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private SecurityUtility hash;
#Autowired
private ClientDetailsService clientDetailsService;
private static final String[] PUBLIC_MATCHERS = { "/", "/css/**", "/image/**", "/js/**", "/newUser",
"/forgetPassword", "/login", "/logout", "/fonts/**", "/signUp", "/register", "/sendEmail", "/logout", "/tes","/oauth2/**","/api/**",
"/admin/tes","/SpringSecurityOAuth2Example/**",
"/admin/tes2" };
private static final String[] ADMIN_MATCHERS = { "/admin", "/admin/**" };
private static final String[] OAUTH2_PAGE = { "/oauth/**", "/api/**" };
private final String USERS_QUERY = "select username, password, is_enabled from user where username=?";
private final String ROLES_QUERY = "select u.username, u.is_enabled, r.name as authority from user u "
+ "inner join user_role ur on (u.id = ur.user_id) " + "inner join role r on (ur.role_id = r.roleid) "
+ "where username=?";
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated().and().formLogin()
.loginPage("/login").loginProcessingUrl("/app-login").usernameParameter("app_username")
.passwordParameter("app_password").defaultSuccessUrl("/myAccount").permitAll()
.and().logout().logoutSuccessUrl("/login")
.permitAll();
http.authorizeRequests().antMatchers(ADMIN_MATCHERS).hasRole("ADMIN");
// http.csrf().disable();
http.csrf().ignoringAntMatchers(OAUTH2_PAGE);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// temporary
// auth.inMemoryAuthentication().withUser("admin").password("admin").roles("test");
auth.jdbcAuthentication().usersByUsernameQuery(USERS_QUERY).authoritiesByUsernameQuery(ROLES_QUERY)
.dataSource(dataSource).passwordEncoder(hash.passwordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
Auth Controller
#RestController
#EnableResourceServer
public class AuthController {
#GetMapping("/api/demo1")
public String apiTes() {
System.out.println("sysout mas");
return "return result";
}
}
solved guys, it because i was using springboot 1.5.10 so i have to add
security.oauth2.resource.filter-order=3
to spring application.properties

spirng boot 2 jwt oauth2 + angular 5 can't get the JWT

I'm new working with spring boot and spring security and I'm trying to implement oauth2 to generate a JWT and used this token in an angular5 application, my situation is that after implementation I can get the token if a use postman or curl but when I use my web client in angular I can't get the token.
this is what I did.
My login method is angular
login(username: string, password: string ) {
const params: HttpParams = new HttpParams();
const headers: Headers = new Headers();
params.set('username', 'GDELOSSANTOS');
params.set('password', 'ADMIN');
params.set('client_id', 'ADMIN');
params.set('client_secret', 'ADMIN');
params.set('grant_type', 'password');
params.set('scope', '*');
headers.set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post(Constante.BACKEND_TOKEN_REQUEST, {headers}, {params} ).subscribe
(res => this.setSession);
}
My authorization server
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(AuthorizationServerConfig.class);
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
logger.traceEntry();
clients
.inMemory()
.withClient(ConstanteUtil.Seguridad.CLIEN_ID)
.secret(Seguridad.CLIENT_SECRET)
.authorizedGrantTypes(Seguridad.GRANT_TYPE_PASSWORD, Seguridad.AUTHORIZATION_CODE, Seguridad.REFRESH_TOKEN, Seguridad.IMPLICIT )
.authorities(UsusarioRoles.ROLE_ADMIN, UsusarioRoles.ROLE_USER)
.resourceIds(resourceId)
.scopes(Seguridad.SCOPE_READ, Seguridad.SCOPE_WRITE, Seguridad.TRUST)
.accessTokenValiditySeconds(Seguridad.ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(Seguridad.FREFRESH_TOKEN_VALIDITY_SECONDS);
logger.info("Configuracion " + clients);
logger.traceExit();
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
My Resource Server
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(AuthorizationServerConfig.class);
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Override
public void configure(final HttpSecurity http) throws Exception {
logger.traceEntry("Entrada configure");
// #formatter:off
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests().anyRequest().permitAll();
logger.info("Ejecucion de metodo " + http);
// #formatter:on
}
#Override
public void configure(final ResourceServerSecurityConfigurer config) {
config.resourceId(resourceId).stateless(true); }
}
The WebSecurity
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(WebSecurityConfig.class);
#Autowired
#Resource(name = "UsuarioService")
private UserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
logger.traceEntry("globalUserDetails", auth);
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
logger.traceExit("globalUserDetails", auth);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
logger.traceEntry();
logger.info("ejecutando configuracion " + http);
http.cors().disable()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/login", "/logout.do").permitAll()
.antMatchers("/**").authenticated()
.and().formLogin().loginPage("/login").permitAll()
.and().httpBasic();
logger.info("se ejecuto configuracion " + http);
}
#Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/auth/token").allowedOrigins("http://localhost:9000");
}
};
}
}
The implementation of loadUserDetail of UserDetailService
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.traceEntry("Iniciando loadUserByUsername");
/Here we are using dummy data, you need to load user data from
database or other third party application/
try {
Usuario usuario = findAllUsuarioRoleByName(username);
logger.info("Se encontro el usaurio " + usuario);
UserBuilder builder = null;
if (usuario != null) {
List<String> roles = new ArrayList<>();
Collection<UsuarioRole> usuarioRoleByUsuarioName = usuarioRoleRepository.findAllUsuarioRoleByUsuarioName(usuario.getNombreUsuario());
logger.info("Roles encontrados " + usuarioRoleByUsuarioName.size());
for(UsuarioRole usuarioRole : usuarioRoleByUsuarioName) {
roles.add(usuarioRole.getRole().getNombreRole());
}
String[] rolesArray = new String[roles.size()];
rolesArray = roles.toArray(rolesArray);
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.password(new BCryptPasswordEncoder().encode(usuario.getClaveUsuario()));
for (String string : rolesArray) {
logger.debug("**** " + string);
}
builder.roles(rolesArray);
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}finally {
logger.traceExit("Finalizando loadUserByUsername");
}
}
Make the following adjustments to your angular code.
Pass client_id and client_secret through Authorization header.
Serialize the object before post (you can reference this answer).
login(username: string, password: string ) {
let body = {
username: 'GDELOSSANTOS',
password: 'ADMIN',
grant_type: 'password'
};
// Serialize body object
let bodySerialized = 'grant_type=password&password=ADMIN&username=GDELOSSANTOS';
let headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Authorization', 'Basic ' + btoa("ADMIN:ADMIN"));
return this.http.post(Constante.BACKEND_TOKEN_REQUEST,
bodySerialized,
{
headers: headers
}).subscribe(res => this.setSession);
}

Resources