Comments (5)
JHipster has completed the sample check
.yo-rc.json
: valid
Entities JDL: blank
Application: successfully generated
Frontend check: success
Backend check: success
E2E check: success
This check uses jhipster info
output from the issue description to generate the sample.
Bug report that does not contain this information will be marked as invalid.
from generator-jhipster.
based on my comparison with 8.1.0 and 7.9.4, these 3 important classes were missing.
The renaming of UserJwtController to AuthenticateController was totally unnecessary.
You should get back those 3 classes, and modify the necessary JWT token verification and the assigning the user principal there.
Suggested fix:
@Component
public class TokenProvider {
private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
private static final String AUTHORITIES_KEY = "auth";
private static final String INVALID_JWT_TOKEN = "Invalid JWT token.";
private final JwtEncoder jwtEncoder;
private final long tokenValidityInMilliseconds;
private final long tokenValidityInMillisecondsForRememberMe;
private final SecurityMetersService securityMetersService;
private final JwtDecoder jwtDecoder;
public TokenProvider(JwtEncoder jwtEncoder, JHipsterProperties jHipsterProperties, SecurityMetersService securityMetersService, JwtDecoder jwtDecoder) {
this.jwtEncoder = jwtEncoder;
this.jwtDecoder = jwtDecoder;
String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret();
if (!ObjectUtils.isEmpty(secret)) {
log.debug("Using a Base64-encoded JWT secret key");
} else {
log.warn(
"Warning: the JWT key used is not Base64-encoded. " +
"We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security."
);
//todo where is this supposed to be assigned to since JwtClaimsSet builder doesnt take signing argument
secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
}
this.tokenValidityInMilliseconds = 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
this.tokenValidityInMillisecondsForRememberMe =
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe();
this.securityMetersService = securityMetersService;
}
public String createToken(Authentication authentication, boolean rememberMe) {
String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));
Instant now = Instant.now();
Instant validity;
if (rememberMe) {
validity = now.plus(this.tokenValidityInMilliseconds, ChronoUnit.SECONDS);
} else {
validity = now.plus(this.tokenValidityInMillisecondsForRememberMe, ChronoUnit.SECONDS);
}
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuedAt(Instant.now())
.expiresAt(validity)
.subject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.build();
JwsHeader jwsHeader = JwsHeader.with(JWT_ALGORITHM).build();
return this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue();
}
public Authentication getAuthentication(String token) {
Jwt jwt = jwtDecoder.decode(token); // decode and verify the token
Map<String, Object> claims = jwt.getClaims();
Collection<? extends GrantedAuthority> authorities = Arrays
.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.filter(auth -> !auth.trim().isEmpty())
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(jwt.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
public boolean validateToken(String authToken) {
try {
Jwt jwt = jwtDecoder.decode(authToken); // decode and verify the token
return true;
} catch (JwtException e) {
this.securityMetersService.trackTokenMalformed();
log.trace(INVALID_JWT_TOKEN, e);
} catch (IllegalArgumentException e) { // TODO: should we let it bubble (no catch), to avoid defensive programming and follow the fail-fast principle?
log.error("Token validation error {}", e.getMessage());
}
return false;
}
}
the SecurityConfiguration can roughly follow the same format as before.
import com.pdfchatter.security.AuthoritiesConstants;
import com.pdfchatter.security.jwt.JwtConfigurer;
import com.pdfchatter.security.jwt.TokenProvider;
import com.pdfchatter.web.filter.SpaWebFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import tech.jhipster.config.JHipsterProperties;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfiguration {
private final JHipsterProperties jHipsterProperties;
private final TokenProvider tokenProvider;
public SecurityConfiguration(JHipsterProperties jHipsterProperties, TokenProvider tokenProvider) {
this.jHipsterProperties = jHipsterProperties;
this.tokenProvider = tokenProvider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
http
.cors(withDefaults())
.csrf(csrf -> csrf.disable())
.addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class)
.headers(headers ->
headers
.contentSecurityPolicy(csp -> csp.policyDirectives(jHipsterProperties.getSecurity().getContentSecurityPolicy()))
.frameOptions(FrameOptionsConfig::sameOrigin)
.referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
.permissionsPolicy(permissions ->
permissions.policy(
"camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()"
)
)
)
.authorizeHttpRequests(authz ->
// prettier-ignore
authz
.requestMatchers(mvc.pattern("/index.html"), mvc.pattern("/*.js"), mvc.pattern("/*.txt"), mvc.pattern("/*.json"), mvc.pattern("/*.map"), mvc.pattern("/*.css")).permitAll()
.requestMatchers(mvc.pattern("/*.ico"), mvc.pattern("/*.png"), mvc.pattern("/*.svg"), mvc.pattern("/*.webapp")).permitAll()
.requestMatchers(mvc.pattern("/assets/**")).permitAll()
.requestMatchers(mvc.pattern("/content/**")).permitAll()
.requestMatchers(mvc.pattern("/swagger-ui/**")).permitAll()
.requestMatchers(mvc.pattern(HttpMethod.POST, "/api/authenticate")).permitAll()
.requestMatchers(mvc.pattern(HttpMethod.GET, "/api/authenticate")).permitAll()
.requestMatchers(mvc.pattern("/api/register")).permitAll()
.requestMatchers(mvc.pattern("/api/activate")).permitAll()
.requestMatchers(mvc.pattern("/api/account/reset-password/init")).permitAll()
.requestMatchers(mvc.pattern("/api/account/reset-password/finish")).permitAll()
.requestMatchers(mvc.pattern("/api/admin/**")).hasAuthority(AuthoritiesConstants.ADMIN)
.requestMatchers(mvc.pattern("/api/**")).authenticated()
.requestMatchers(mvc.pattern("/websocket/**")).authenticated()
.requestMatchers(mvc.pattern("/v3/api-docs/**")).hasAuthority(AuthoritiesConstants.ADMIN)
.requestMatchers(mvc.pattern("/management/health")).permitAll()
.requestMatchers(mvc.pattern("/management/health/**")).permitAll()
.requestMatchers(mvc.pattern("/management/info")).permitAll()
.requestMatchers(mvc.pattern("/management/prometheus")).permitAll()
.requestMatchers(mvc.pattern("/management/**")).hasAuthority(AuthoritiesConstants.ADMIN)
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(exceptions ->
exceptions
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler())
).apply(securityConfigurerAdapter());
//todo need to clarify what is this for
// .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}
private JwtConfigurer securityConfigurerAdapter() {
return new JwtConfigurer(tokenProvider);
}
}
and the new JWTConfigurer
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class JwtConfigurer implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;
public JwtConfigurer(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void init(HttpSecurity builder) throws Exception {
JwtFilter customFilter = new JwtFilter(tokenProvider);
builder.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(HttpSecurity http) {
JwtFilter customFilter = new JwtFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
and the new AuthenticateController(formerly known as the UserJWTController)
import com.fasterxml.jackson.annotation.JsonProperty;
import com.pdfchatter.security.jwt.TokenProvider;
import com.pdfchatter.web.rest.vm.LoginVM;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
/**
* Controller to authenticate users.
*/
@RestController
@RequestMapping("/api")
public class AuthenticateController {
private final Logger log = LoggerFactory.getLogger(AuthenticateController.class);
private final TokenProvider tokenProvider;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public AuthenticateController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
this.tokenProvider = tokenProvider;
this.authenticationManagerBuilder = authenticationManagerBuilder;
}
@PostMapping("/authenticate")
public ResponseEntity<JWTToken> authorize(@Valid @RequestBody LoginVM loginVM) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(),
loginVM.getPassword()
);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.createToken(authentication, loginVM.isRememberMe());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setBearerAuth(jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}
/**
* {@code GET /authenticate} : check if the user is authenticated, and return its login.
*
* @param request the HTTP request.
* @return the login if the user is authenticated.
*/
@GetMapping("/authenticate")
public String isAuthenticated(HttpServletRequest request) {
log.debug("REST request to check if the current user is authenticated");
return request.getRemoteUser();
}
/**
* Object to return as body in JWT Authentication.
*/
static class JWTToken {
private String idToken;
JWTToken(String idToken) {
this.idToken = idToken;
}
@JsonProperty("id_token")
String getIdToken() {
return idToken;
}
void setIdToken(String idToken) {
this.idToken = idToken;
}
}
}
I'll leave it to you guys to revert some changes and modify them accordingly. Lemme know your thoughts on whether my new suggestions are sound. I just tested with another sample project and it works. @mraible
from generator-jhipster.
For jwt you should use:
@AuthenticationPrincipal org.springframework.security.oauth2.jwt.Jwt jwt
from generator-jhipster.
We moved away from custom jwt handling to spring-boot provided one.
We now rely on spring-boot default behavior for jwt, so this is working as expected.
from generator-jhipster.
There is nothing to do on JHipster side.
from generator-jhipster.
Related Issues (20)
- Improve sonar ratings. HOT 2
- Neo4j dev database type needs Neo4j Harness just like how relational dev database types have h2 HOT 4
- jhipster k8s generator not working HOT 1
- WebSocketMessageBrokerConfigurer not found HOT 2
- Improvements to patch implementation.
- Error generating app: `Cannot read properties of undefined (reading 'config')`
- JUnit integration tests are too slow. HOT 9
- Liquibase Neo4j as an alternative to Neo4j migration in Neo4j database flavor HOT 5
- Zero sonar code smells at ng-default HOT 5
- jHipster 8.1.0 Vue Postgres Elasticsearch variant still needs a spring.jpa.database-platform (dialect) to be assigned to it. Application failed to start HOT 5
- Issue with v8.1.0 when running on docker throws [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: BEFORE_TRANSACTION_COMPLETION HOT 8
- jHipster 8.1.0 JDL generated @OneToOne and @ManyToOne relationship was annotated with FetchType.LAZY, getting the attribute of the entity causes LazyInitializationException No Session HOT 3
- Websockets admin/tracker generated example is broken in jHipster 8.1.0 HOT 5
- JHipster 8.2.0 release HOT 37
- How to dynamically change schemas using SchemaContextHolder and SchemaSettingAspect classes when passing the schema in the header of the request
- Incremental changelog won't count up timestamp or won't use the current timestamp but always the creation timestamp leading to incorrectly overwritten changelog files
- Active session functionality does not work for session-based authentication HOT 2
- Eclipsestore Support HOT 2
- JDL generation with builtInEntity on authority in jHipster 8.1.0 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from generator-jhipster.