jhipster / generator-jhipster

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
https://www.jhipster.tech
Apache License 2.0
21.5k stars 4.02k forks source link

JWT Authentication can't resolve `?#{principal.username}` when used in queries #24374

Closed mraible closed 10 months ago

mraible commented 10 months ago
Overview of the issue

I started updating my jhipster8-demo project today to QA everything before the 8.1.0 release. To add more security around blogs and entries, I modified BlogResource.java and the getAllBlogs() method.

Before:

return blogRepository.findAll();

After:

return blogRepository.findByUserIsCurrentUser();

The findByUserIsCurrentUser() method is generated by JHipster in the BlogRepository class.

public interface BlogRepository extends JpaRepository<Blog, Long> {
    @Query("select blog from Blog blog where blog.user.login = ?#{principal.username}")
    List<Blog> findByUserIsCurrentUser();

When I try to fetch the blogs from the UI, the error is:

2023-11-27T07:09:05.963-07:00 ERROR 66062 --- [  XNIO-1 task-3] o.jhipster.blog.web.rest.BlogResource    : 
 Exception in getAllBlogs() with cause = 'NULL' and exception = 'EL1008E: Property or field 'username' cannot be 
 found on object of type 'org.springframework.security.oauth2.jwt.Jwt' - maybe not public or not valid?' 

org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'username' cannot be 
  found on object of type 'org.springframework.security.oauth2.jwt.Jwt' - maybe not public or not valid?
Motivation for or Use Case

This worked with JHipster 7.

Reproduce the error

Here's my .yo-rc.json:

{
  "generator-jhipster": {
    "applicationType": "monolith",
    "authenticationType": "jwt",
    "baseName": "blog",
    "buildTool": "maven",
    "cacheProvider": "ehcache",
    "clientFramework": "angular",
    "clientTestFrameworks": ["cypress"],
    "clientTheme": "none",
    "creationTimestamp": 1701088333195,
    "cypressAudit": true,
    "cypressCoverage": true,
    "databaseType": "sql",
    "devDatabaseType": "h2Disk",
    "devServerPort": 4200,
    "enableGradleEnterprise": null,
    "enableHibernateCache": true,
    "enableSwaggerCodegen": false,
    "enableTranslation": true,
    "entities": [],
    "gradleEnterpriseHost": null,
    "jhipsterVersion": "8.0.0",
    "languages": ["en", "es"],
    "messageBroker": false,
    "microfrontend": null,
    "microfrontends": [],
    "nativeLanguage": "en",
    "packageName": "org.jhipster.blog",
    "prodDatabaseType": "postgresql",
    "reactive": false,
    "searchEngine": false,
    "serverPort": null,
    "serverSideOptions": [],
    "serviceDiscoveryType": false,
    "testFrameworks": ["cypress"],
    "websocket": false,
    "withAdminUi": true
  }
}

And the JDL:

entity Blog {
  name String required minlength(3)
  handle String required minlength(2)
}

entity Post {
  title String required
  content TextBlob required
  date Instant required
}

entity Tag {
  name String required minlength(2)
}

relationship ManyToOne {
  Blog{user(login)} to User with builtInEntity
  Post{blog(name)} to Blog
}

relationship ManyToMany {
  Post{tag(name)} to Tag{post}
}

paginate Post, Tag with infinite-scroll
Suggest a Fix

Spring Security's docs seem to indicate a SecurityEvaluationContextExtension bean is needed. I tried adding one to SecurityConfiguration.java, but it does not help:

@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
    return new SecurityEvaluationContextExtension();
}
JHipster Version(s)

8.0.0

mshima commented 10 months ago

Maybe ?#{authentication.name}?

mraible commented 10 months ago

@mshima Yes, this seems to work as expected. I'll create a PR.

kevintanhongann commented 7 months ago

The issue that I was having with jHipster 8.1.0 is that the @AuthenticationPrincipal UserDetails userDetails doesn't work as intended anymore (it's null), no matter which variant that I use.

 @PostMapping("/upload")
    @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.GROUP_USER + "\")")
    public ResponseEntity<TextExtractionResponse> uploadFile(
        @RequestParam("file") MultipartFile multipartFile,
        @AuthenticationPrincipal UserDetails userDetails
    ) throws IOException {
        Optional<User> userOptional = userService.findOneByLogin(userDetails.getUsername());
        Optional<UserCompany> userCompanyOptional = userCompanyService.findByUser(userOptional.get());
        UserCompany userCompany = userCompanyOptional.get();
        ...
}        

This is bad...