(Spring Boot + JWT) how to get JWT in response? - java

I want to get the JWT in respose when I hit '/login'. But I am missing something and can't figure it out.
Following is my code:
SecurytiApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#EnableJpaRepositories
#ComponentScan(basePackages = "com.example.securyti")
#EntityScan(basePackages = "com.example.securyti")
#SpringBootApplication
public class SecurytiApplication {
public static void main(String[] args) {
SpringApplication.run(SecurytiApplication.class, args);
}
}
SecurityConfig.java
package com.example.securyti.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.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.example.securyti.security.UserAccountService;
import com.example.securyti.security.jwt.JwtAuthenticationFilter;
import com.example.securyti.security.jwt.JwtAuthorizationFilter;
import com.example.securyti.security.jwt.JwtTokenService;
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
protected JwtAuthenticationFilter jwtAuthenticationFilter;
protected JwtAuthorizationFilter JwtAuthorizationFilter;
#Autowired
protected UserAccountService userAccountService;
#Autowired
protected JwtTokenService jwtTokenService;
#Autowired
protected ConfigurationService configService;
#Override
protected void configure(HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = authenticationManager();
jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager,
jwtTokenService, (BCryptPasswordEncoder) passwordEncoder(), userAccountService);
JwtAuthorizationFilter = new JwtAuthorizationFilter(authenticationManager,
configService, jwtTokenService);
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/register")
.permitAll()
.anyRequest().authenticated().and().addFilter(jwtAuthenticationFilter).addFilter(JwtAuthorizationFilter);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userAccountService).passwordEncoder(passwordEncoder());
}
}
JwtAuthenticationFilter.java
package com.example.securyti.security.jwt;
import java.io.IOException;
import java.util.Collections;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;
import com.example.securyti.security.UserAccount;
import com.example.securyti.security.UserAccountService;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
private final AuthenticationManager authenticationManager;
private final JwtTokenService jwtTokenService;
private final BCryptPasswordEncoder passwordEncoder;
private final UserAccountService userAccountService;
public JwtAuthenticationFilter(
final AuthenticationManager authenticationManager,
final JwtTokenService jwtTokenService,
final BCryptPasswordEncoder passwordEncoder,
final UserAccountService userAccountService) {
this.authenticationManager = authenticationManager;
this.jwtTokenService = jwtTokenService;
this.passwordEncoder = passwordEncoder;
this.userAccountService = userAccountService;
}
#Override
public Authentication attemptAuthentication(final HttpServletRequest req,
final HttpServletResponse res) {
String jwt = jwtTokenService.getTokenFromRequest(req);
UserAccount userAccount = null;
if (StringUtils.hasText(jwt) && jwtTokenService.validateToken(jwt)) {
userAccount = (UserAccount) userAccountService.loadUserByUsername(jwtTokenService.getUsernameFromJWT(jwt));
}
if(userAccount == null){
throw new BadCredentialsException("Bad credentials");
}
AbstractAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userAccount.getUsername(),
userAccount.getPassword(), Collections.emptyList());
Authentication auth = authenticationManager.authenticate(authToken);
return auth;
}
private String getUsername(final UserAccount creds) {
if (creds != null) {
return creds.getUsername();
}
return null;
}
#Override
protected void successfulAuthentication(final HttpServletRequest req,
final HttpServletResponse res, final FilterChain chain,
final Authentication auth) throws IOException, ServletException {
final UserAccount account = (UserAccount) auth.getPrincipal();
jwtTokenService.addTokenToResponse(account, res);
super.successfulAuthentication(req, res, chain, auth);
}
}
JwtAuthorizationFilter.java
package com.example.securyti.security.jwt;
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.example.securyti.config.ConfigurationService;
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private ConfigurationService configService;
private JwtTokenService jwtTokenService;
public JwtAuthorizationFilter(AuthenticationManager authManager, ConfigurationService configService,
final JwtTokenService jwtTokenService) {
super(authManager);
this.configService = configService;
this.jwtTokenService = jwtTokenService;
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(configService.getHeaderField());
if (header == null || !header.startsWith(configService.getTokenPrefix())) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(configService.getHeaderField());
if (token != null) {
// parse the token.
String user = jwtTokenService.getUsernameFromJWT(token);
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
JwtTokenService.java (This is just the helper class)
package com.example.securyti.security.jwt;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.example.securyti.config.ConfigurationService;
import com.example.securyti.security.UserAccount;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
#Service
public class JwtTokenService {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenService.class);
private ConfigurationService configurationService;
public JwtTokenService(final ConfigurationService configurationService) {
super();
this.configurationService = configurationService;
}
String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader(configurationService.getHeaderField());
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(configurationService.getTokenPrefix())) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
public void addTokenToResponse(UserAccount account, HttpServletResponse res) {
LocalDateTime expiry = LocalDateTime.now().plusSeconds(configurationService.getJwtExpirationInSec());
String token = Jwts.builder()
.setSubject(account.getUsername())
.setIssuedAt(new Date())
.setExpiration(Date.from(expiry.atZone(ZoneId.systemDefault()).toInstant()))
.signWith(SignatureAlgorithm.HS512, configurationService.getJwtSecret())
.compact();
res.addHeader(configurationService.getHeaderField(), configurationService.getTokenPrefix() + token);
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(configurationService.getJwtSecret())
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(configurationService.getJwtSecret()).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
}
application.properties
spring.datasource.url= jdbc:mysql://localhost:3306/mydb
spring.datasource.username= root
spring.datasource.password= root
spring.jpa.hibernate.ddl-auto = update
#TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
logging.level.root=DEBUG
## JWT
jwt.secret= JWTSuperSecretKey
jwt.expirationInSec = 10
jwt.tokenPrefix = Bearer
jwt.headerField = Authorization
There is no handler method '\login' in controller. Currently when I hit the '/login' with valid username and password I get 403 with following message on console:
Bad credentials
at com.example.securyti.security.jwt.JwtAuthenticationFilter.attemptAuthentication(JwtAuthenticationFilter.java:58)
What am I missing. Please correct me if my understanding happen to be wrong somewhere. Thanks in advance.

for this purpose probably use Spring Cloud security with JWT token is a more suitable choice. For your use case you should configure an authorization server and this job is very simple in spring cloud security the server will be a spring boot app like below:
#Configuration
#EnableAuthorizationServer
public class SecurityOAuth2AutorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final PasswordEncoder passwordEncoder;
public SecurityOAuth2AutorizationServerConfig(AuthenticationManager authenticationManager,
PasswordEncoder passwordEncoder) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
}
#Bean
public UserDetailsService accountUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(new User("user", passwordEncoder.encode("secret"),
Collections.singleton(new SimpleGrantedAuthority("USER"))));
return inMemoryUserDetailsManager;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.approvalStoreDisabled()
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.userDetailsService(accountUserDetailsService)
.reuseRefreshTokens(false);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()")
.passwordEncoder(passwordEncoder)
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid")
.authorities("ROLE_USER", "ROLE_EMPLOYEE")
.scopes("read", "write", "trust", "openid")
.resourceIds("oauth2-resource")
.autoApprove(true)
.accessTokenValiditySeconds(5)
.refreshTokenValiditySeconds(60*60*8);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
}
WebSecurityConfig class
#Configuration
#Order(SecurityProperties.DEFAULT_FILTER_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().disable()
.formLogin().loginPage("/login").loginProcessingUrl("/login")
.permitAll()
.and()
.requestMatchers().antMatchers("/account/userInfo", "/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jwt-authserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jwt-authserver</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and a login page like below:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" th:href="#{/webjars/bootstrap/3.3.7-1/css/bootstrap.css}"/>
<link rel="stylesheet" th:href="#{/webjars/bootstrap/3.3.7-1/css/bootstrap-theme.css}"/>
<title>Log In</title>
</head>
<body>
<div class="container">
<form role="form" action="login" method="post">
<div class="row">
<div class="form-group">
<div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-">
<label for="username">Username:</label>
<input type="text" class="form-control" id="username" name="username"/>
</div>
</div>
</div>
<div class="row">
<div class="form-group">
<div class="col-md-6 col-lg-6 col-md-offset-2 col-lg-offset-2">
<label for="password">Password:</label>
<input type="password" class="form-control" id="password" name="password"/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-offset-2 col-lg-offset-2 col-lg-12">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
<script th:src="#{/webjars/jquery/3.2.0/jquery.min.js}"></script>
<script th:src="#{/webjars/bootstrap/3.3.7-1/js/bootstrap.js}" ></script>
</body>
</html>
in this way you have a complete authorization server oauth2 that use JWT token even if it can to seem a too big effort it is in my opinion the correct way pleas do not use custom security protocol or path because those will became very soon hard to maintain and complex to fix security issues use a standard security protocol is always the best way!
on the client side you can implement by your self a web sso using the authorization code or password oauth2 standard flow or using Spring cloud security on the client application using the WebSSO capabilities with a simple configuration like below:
#Configuration
#EnableOAuth2Sso
class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and().httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
I hope that it can help you

Related

No plugin found for delimiter text/html;charset=UTF-8! Registered plugins: [org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer#

I need your help on resolution for the following Exception:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Apr 19 09:45:23 CEST 2020
There was an unexpected error (type=Internal Server Error, status=500).
No plugin found for delimiter text/html;charset=UTF-8! Registered plugins: [org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer#665cd8b1].
java.lang.IllegalArgumentException: No plugin found for delimiter text/html;charset=UTF-8! Registered plugins: [org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer#665cd8b1].
at org.springframework.plugin.core.SimplePluginRegistry.lambda$getRequiredPluginFor$2(SimplePluginRegistry.java:140)
...
I implemented two WebServices using SpringBoot:
a Data Service called ProductMicroService and
an Interface Service called ProductMicroServiceClient for consumers at front-ends.
The ProductMicroService is implemented on basis of JPA and uses as persistency back-end a SQL database (in my sample a MariaDB).
The Controller provides RESTful API end points in JSON with media support (HATEOAS).
The ProductMicroServiceClient consumes the API end points from ProductMicroService and provides a RESTful API for front-ends also with media support (HATEOAS).
In my samples the client is a WebBrowser running some simple Thymleaf Templates.
When running the pure ProductMicroService and ProductMicroServiceClient implementations on my local machine everything goes well.
Also after introducing security based on JDBC for the ProductMicroServiceClient everything runs fine including access restrictions on the API end-points.
The User and Authority tables are persisted at the same MariaDB as for the Data Service.
But after introducing SecurityService for the ProductMicroService I receive the Exception above after successful authentication (standard login page from SpringBoot).
I am using OpenJDK.
I couldn’t find any direction for a solution when searching in internet.
Some relevant code
for ProductMicroServiceClient:
———— pom.xml ———————————————————————————————————
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>eu.mydomain</groupId>
<artifactId>ProductMicroServiceClient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ProductMicroServiceClient</name>
<description>Learn Spring Boot Configuration for SpringData</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
——— eu.mydomain.product.security.EncodingFilter.java ————————————————
package eu.mydomain.product.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.filter.GenericFilterBean;
public class EncodingFilter extends GenericFilterBean {
#Override
public void doFilter( ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter( request, response);
}
}
——— eu.mydomain.product.security.WebSecurityConfig.java —————————————
package eu.mydomain.product.security;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure( HttpSecurity http) throws Exception {
http.addFilterBefore( new EncodingFilter(), ChannelProcessingFilter.class);
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/product/**","/products", "/products/**").hasRole("USER")
.antMatchers("/create-products", "/products4create", "/products4edit/**","/update-products/**","/products4edit/**","/delete-products/**","/products4delete/**").hasRole("ADMIN")
// .antMatchers("/","/**").permitAll()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
// .and().httpBasic()
;
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(16);
}
#Autowired DataSource dataSource;
public void configure( AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.passwordEncoder( encoder() )
.usersByUsernameQuery( "SELECT username, password, enabled FROM users WHERE username = ?")
.authoritiesByUsernameQuery( "SELECT username, authority FROM authorities WHERE username = ?")
.dataSource( dataSource);
}
}
As said until now everything works as expected.
For the ProductMicroService
I introduced a database View V_PRODUCT_USERS that provides relevant user authorities from the users and authorities tables and implemented a ProductUser Entity, IProductUserRepository and UserDetailService.
——— eu.mydomain.product.domain.ProductUser.java —————————————
package eu.mydomain.product.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table( name="v_product_users")
public class ProductUser {
/*
* create view if not exists v_product_users as select u.is id, u.username username, u.'password' 'password', a.authority rolefrom users u, authorities a where u.username = a.username;
* commit;
*/
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private Long id;
#Column(nullable = false, unique = true, updatable = false)
private String username;
#Column(nullable = false, updatable = false)
private String password;
#Column(nullable = false, updatable = false)
private String role;
public ProductUser()
{}
public ProductUser(Long id, String username, String password, String role)
{
super();
this.id = id;
this.username = username;
this.password = password;
this.role = role;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
——— eu.mydomain.product.repository.IProductUserRepository.java ——————————
package eu.mydomain.product.repository;
import org.springframework.data.repository.CrudRepository;
import eu.mydomain.product.domain.ProductUser;
public interface IProductUserRepository extends CrudRepository< ProductUser, Long> {
ProductUser findByUsername( String username);
}
——— eu.mydomain.product.service.UserDetailServiceImpl.java ——————————
package eu.mydomain.product.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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 eu.mydomain.product.domain.ProductUser;
import eu.mydomain.product.repository.IProductUserRepository;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private IProductUserRepository userRepository;
#Override
public UserDetails loadUserByUsername( String username) throws UsernameNotFoundException {
ProductUser productUser = userRepository.findByUsername( username);
return new User(
username,
productUser.getPassword(),
AuthorityUtils.createAuthorityList( productUser.getRole()) // #TODO: comma separated list of all roles granted
);
}
}
Finally, I introduced for security an EncodingFilter and a WebSecurityConfig:
——— eu.mydomain.product.security.EncodingFilter.java ————————————————
package eu.mydomain.product.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.filter.GenericFilterBean;
public class EncodingFilter extends GenericFilterBean {
#Override
public void doFilter( ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter( request, response);
}
}
——— eu.mydomain.product.security.WebSecurityConfig.java ————————————————
package eu.mydomain.product.security;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import eu.nydomain.product.service.UserDetailsServiceImpl;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
#Override
protected void configure( HttpSecurity http) throws Exception {
/*
* For Tyhmleaf Templates add <meta>-tag to the HTML-Header for the CSRF Token
*
* <meta name="_csrf" th:content="${_csrf.token}" />
* <meta name="_csrf_header" th:content="${_csrf.headerName}" />
*/
http.addFilterBefore( new EncodingFilter(), ChannelProcessingFilter.class);
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic()
;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(16);
}
#Autowired
#Override
public void configure( AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService( userDetailsServiceImpl)
.passwordEncoder( encoder() );
}
}
Now, after introducing Security to the Data Service I get the Exception after successful authentication by SpringBoot Security and while before loading the following page.
<!DOCTYPE html5>
<html>
<head>
<title>Spring Boot Introduction Sample - Products</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="_csrf" th:content="${_csrf.token}" />
<meta name="_csrf_header" th:content="${_csrf.headerName}" />
<!-- <link rel="stylesheet" type="text/css" media="all" href="../css/my.css" data-th-href="#{/css/my.css}" /> -->
</head>
<body>
<p>This is a <i>Product</i> database - as a Client for the Spring Boot Test Sample for RESTful Product services</p>
<table>
<tr>
<td>
<form action="#" th:action="#{/products}" th:object="${product}" method="get">
<input type="submit" value="Show All Products" />
</form>
</td>
<td>
<form action="#" th:action="#{/products4create}" th:object="${product}" method="get">
<input type="submit" value="Create a new Product" />
</form>
</td>
</tr>
</table>
<hr/>
<p>All Products:</p>
<table>
<thead>
<tr>
<th>Id</th>
<th>Product id</th>
<th>Product Name</th>
<th>Product Type</th>
<th>Description</th>
<th>Brand</th>
<th colspan="2">Action</th>
</tr>
</thead>
<tbody>
<!-- <tr th:each="product, rowStat: ${products}" th:style="${rowStat.odd} ? 'color: gray' : 'color: blue;'"> -->
<tr th:each="product : ${products}">
<td th:text="${product.content.id}">1</td>
<td th:text="${product.content.prodId}">Prod Id</td>
<td th:text="${product.content.name}">Name</td>
<td th:text="${product.content.type}">Type</td>
<td th:text="${product.content.description}">Description</td>
<td th:text="${product.content.brand}">Brand</td>
<td><a th:href="#{|/products4edit/${product.content.id}|}">Edit</a></td>
<td><a th:href="#{|/products4delete/${product.content.id}|}">Delete</a></td>
</tr>
</tbody>
</table>
</body>
</html>
the mention Exception at the top of this question.
What I tried already:
Put the different UTF-8 configurations to files, pom.xml etc.
Changed database fields to CHARSET utf8 COLLATE utf8_bin and also CHARSET utf8mb4 COLLATE utf8mb4_bin
I implemented my personal login page (and related handling)
I identified that the ProductMicroServiceClient after authentication works until calling the ProductMicroService API end-point via:
...
CollectionModel<EntityModel<Product>> productResources = myTraverson
.follow( "/products") // JSON element
.toObject(new ParameterizedTypeReference<CollectionModel<EntityModel<Product>>>() {});
...
which doesn't enter the API end-point:
#GetMapping(value = "/products", produces = "application/hal+json")
public CollectionModel<ProductRepresentationModel> findAll() {
List<Product> products = new ArrayList<>();
productRepository.findAll().forEach( (p -> products.add(p)));
CollectionModel<ProductRepresentationModel> productsModelList = new ProductRepresentationAssembler().toCollectionModel(products);
productsModelList.add( WebMvcLinkBuilder.linkTo( WebMvcLinkBuilder.methodOn(ProductController.class).findAll()).withRel("/products"));
return productsModelList;
}
I introduced at the ProductMicroService an access denied handler
#Override
protected void configure( HttpSecurity http) throws Exception {
/*
* For Tyhmleaf Templates add <meta>-tag to the HTML-Header for the CSRF Token
*
* <meta name="_csrf" th:content="${_csrf.token}" />
* <meta name="_csrf_header" th:content="${_csrf.headerName}" />
*/
http.addFilterBefore( new EncodingFilter(), ChannelProcessingFilter.class);
http
// .httpBasic()
// .and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and()
.httpBasic()
;
}
I switched for debugging to call the the API end-points of the ProductMicrosService using Postman App with Basic-Authentication.
While debugging I identified that
(a) the correct user, (encrypted) password and role(s) get used for authentication
(b) no access denied handler gets called
(c) Method Call for the API end point ( findAll() method ) doesn't get entered
(d) Response Header contains "HttP 401 - Unauthorized"
(e) Response in Postman is empty
I assume now that the Exception above gets thrown as result of receiving an empty response and HttP 401 Unauthorizedfrom the API call.
Question to me now:
Is there something missing or wrong with the security configuration?
Why don't I get an Unauthorized Exception?
The issue with the reported Exception could be resolved now.
After certain changes in configuration of the Security the Exception has been resolved.
Calling the Data Service API End Point (ProductMicroService) from Postman App works now and provides as response the expected JSON Object.
Calling the Data Service API End Point (ProductMicroService) from a Interface Service (ProdctMicroServiceClient) throws another Exception:
401 : [{"timestamp":"2020-04-24T19:22:48.851+0000","status":401,"error":"Unauthorized","message":"Unauthorized","path":"/my-products/products"}]
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 : [{"timestamp":"2020-04-24T19:22:48.851+0000","status":401,"error":"Unauthorized","message":"Unauthorized","path":"/my-products/products"}]
I am now looking after JWT implementation (works well at the ProductMicroService) and OAuth2 (learning in progress).

Spring Security Authentication issue: HTTP 401

I've encountered a bizarre situation using spring security. Having used:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
With following simple security configuration:
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
UserDetails user = User.builder().username("1").password("1").roles("USER").build();
auth.inMemoryAuthentication().withUser(user).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/inquiry").authenticated().anyRequest().permitAll().and()
.httpBasic();
}
}
I constantly get the 401 Http Status code. But I dig deeper into the code and I've realized that in the spring security core there is a minor issue.
The class DaoAuthenticationProvider tries to check if the provided password matches the actual credential with password encoder(in my case BCrypt) in hand. So
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword()))
But in the encoder, the method signature of matches is:
public boolean matches(CharSequence rawPassword, String encodedPassword)
So the authentication fails.
When you use in-memory authentication with BCrypt in your security configuration, you need to encrypt the password string first.
So you can try
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// First encrypt the password string
String encodedPassword = passwordEncoder().encode("1");
// Set the password
UserDetails user = User.builder()
.username("1")
.password(encodedPassword)
.roles("USER")
.build();
// Use in-memory authentication with BCryptEncoder
auth.inMemoryAuthentication()
.withUser(user)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

Spring authentication with JWT returning 403

I am trying to use Spring Security in my REST API using JWT tokens but everytime I am trying to make a login using the endpoint: /login of my Api, I am getting a 403 Forbidden and I have no idea why, with a completely empty body message.
This is my WebSecurity extenson configuration:
package com.debtServer.authentication;
import org.springframework.http.HttpMethod;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.context.annotation.Bean;
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
And the following two classes are the two filteres configured:
package com.debtServer.authentication;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(SecurityConstants.HEADER_STRING);
if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SecurityConstants.SECRET.getBytes())
.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Second filter:
package com.debtServer.authentication;
import com.debtServer.model.LoginBindingModel;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import static com.debtServer.authentication.SecurityConstants.*;
import com.debtServer.persistence.entity.*;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
LoginBindingModel loginCredentials = new ObjectMapper()
.readValue(req.getInputStream(), LoginBindingModel.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginCredentials.getUsername(),
loginCredentials.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
And finally my implementation of the user details:
package com.debtServer.authentication;
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 com.debtServer.persistence.repository.UserRepository;
import com.debtServer.persistence.entity.*;
import java.util.List;
import static java.util.Collections.emptyList;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository applicationUserRepository;
public UserDetailsServiceImpl(UserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity applicationUser = applicationUserRepository.findByNEmail(username);
if (applicationUser == null) {
throw new UsernameNotFoundException(username);
}
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}
I am using Spring Security.
Update: I have found an error in my console:
java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) ~[na:na]
at io.jsonwebtoken.impl.Base64Codec.encode(Base64Codec.java:21) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.Base64UrlCodec.encode(Base64UrlCodec.java:22) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtBuilder.base64UrlEncode(DefaultJwtBuilder.java:349) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtBuilder.compact(DefaultJwtBuilder.java:295) ~[jjwt-0.9.0.jar:0.9.0]
at com.debtServer.authentication.JWTAuthenticationFilter.successfulAuthentication(JWTAuthenticationFilter.java:58) ~[classes/:na]
Line 58 is this one: .compact();
I have just find out the problem. The problem was I was using Java 9, which seems not to be compatible with some of the extensions I was using. I changed to 8 and it is now ok.
Maybe you missed csrf token. Spring security default csrf configuration value is enabled and without csrf token spring security returns 403 status.
Here's what is csrf and how to add csrf token. :
https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-using
Add the dependency inside pom.xml below to solve the problem.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>

Angular 5 + Spring Boot+ Spring Security (Login) [duplicate]

This question already has answers here:
Spring Security: configure(AuthenticationManagerBuilder auth)
(1 answer)
How to configure CORS in a Spring Boot + Spring Security application?
(22 answers)
'Access-Control-Allow-Origin' error in Spring MVC + Zepto POST
(4 answers)
Closed 5 years ago.
IF I login with correct username and password(as in my database),i am getting this error.
Error on Console(Browser)
: Failed to load http://localhost:8081/Documentprocess/api/users/login: Redirect from 'http://localhost:8081/Documentprocess/api/users/login' to 'http://localhost:8081/Documentprocess/api/users/login?error=true' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.**
Login.html
<div class="container">
<h1>Login</h1>
<form [formGroup]="form" (ngSubmit)="signIN()">
<div class="row">
<div class="form-group col-lg-6">
<div class="form-group" [ngClass]="{'has-error':!form.controls['userID'].valid && form.controls['userID'].touched}">
<label>USER ID:</label>
<input class="form-control" type="text" placeholder="John" [formControl]="form.controls['userID']" [(ngModel)]="userID">
<!-- The hasError method will tell us if a particular error exists -->
<div *ngIf="form.controls['userID'].hasError('required') && form.controls['userID'].touched"
class="alert alert-danger">Please Enter your USERID.</div>
</div>
<div class="form-group" [ngClass]="{'has-error':!form.controls['passWord'].valid && form.controls['passWord'].touched}">
<label>PASSWORD:</label>
<input class="form-control" type="password" [formControl]="form.controls['passWord']" [(ngModel)]="passWord">
<!-- The hasError method will tell us if a particular error exists -->
<div *ngIf="form.controls['passWord'].hasError('required') && form.controls['passWord'].touched"
class="alert alert-danger">Please Enter Your Password.</div>
</div>
Login Component.ts
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import {Login} from "./shared/login";
import { LoginService } from './shared/login.service'
//import { BasicValidators } from '../../shared/basic-validators';
import { FormBuilder, FormGroup, Validators, FormArray, FormControl } from '#angular/forms';
import {NgSelectModule} from '#ng-select/ng-select';
import { BrowserModule } from '#angular/platform-browser';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
private url: string = "http://localhost:8081/Documentprocess/api/users";
form: FormGroup;
title: string;
userID: string;
passWord: string;
name:string;
loading = false;
error = '';
searchForm: FormGroup;
constructor(formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private loginService: LoginService,
) {
this.form = formBuilder.group({
userID: ['', [
]],
passWord: ['', [
]],
});
}
ngOnInit() {
}
signIN() {
var result,
loginValue = this.form.value;
result = this.loginService.login(this.userID,this.passWord);
result.subscribe();
}
}
login.service.ts
import { Injectable } from '#angular/core';
import {Http, Response, Headers, RequestOptions} from "#angular/http";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import {Login} from "./login";
#Injectable()
export class LoginService {
private url: string = "http://localhost:8081/Documentprocess/api/users";
constructor(private http: Http) { }
login(userID: string ,passWord: string) {
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
let options = new RequestOptions({ headers: headers });
console.log(userID);
console.log(passWord);
console.log(this.url+'/login');
return this.http.post(this.url+'/login', {username: userID,password: passWord}, options)
.map(res => res.json());
// console.log(user);
}
}
Spring Security Configuration
package com.selfrespect.document.configuration;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.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.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource datasource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
System.out.println(datasource);
auth.
jdbcAuthentication().dataSource(datasource)
.usersByUsernameQuery("select userid,password from users where userid=?")
.authoritiesByUsernameQuery("select role from users where userid=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("I am here");
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/api/services").permitAll()
.antMatchers("/api/users/**").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/api/users/login").failureUrl("/api/users/login?error=true")
.defaultSuccessUrl("/api/users/login_sucess")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
}
MY SQL

Oauth2 Spring implementation

Im am new in Spring. I would like use Oauth2 with Spring Security.
This is my app:
package demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
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;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/"),
new AntPathRequestMatcher("/admin/beans")
))
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('read')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("id");
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust").resourceIds("id")
.accessTokenValiditySeconds(60).and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust").resourceIds("id")
.redirectUris("http://anywhere?key=value").and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("password")
.authorities("ROLE_CLIENT").scopes("read", "write")
.resourceIds("id")
.secret("secret");
}
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password")
.roles("USER").and().withUser("admin").password("password")
.roles("USER");
}
}
}
I want to use password grant authentication. Unfortunately when I run such command:
curl -u my-client-with-secret: http://localhost:8080/oauth/token -d grant_type=password&username=user&password=password&client_id=my-trusted-client&client_secret=secret
the response is:
{"timestamp":1477484999849,"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/oauth/token"}
Can you help me with this problem?
You have to change the curl Request a little. Please put the credentials from the client in front of the Request and append the user credentials as parameters:
curl my-client-with-secret:secret#localhost:8080/oauth/token -d grant_type=password -d username=user -d password=password

Categories

Resources