为两个实体创建JWT安全性。

huangapple go评论101阅读模式
英文:

Create JWT security for two entities

问题

我有两个实体作为我的Spring应用中的两种账户类型:
顾客:

  1. @Entity
  2. @Table(name = "customer")
  3. public class Customer {
  4. @Id
  5. @GeneratedValue(generator = "uuid")
  6. @GenericGenerator(name = "uuid", strategy = "uuid2")
  7. @Column(name = "customer_id",updatable = false)
  8. private String customerId;
  9. // 其他字段...
  10. // 获取和设置方法
  11. }

专家:

  1. @Entity
  2. @Table(name = "specialist")
  3. public class Specialist {
  4. @Id
  5. @GeneratedValue(generator = "uuid")
  6. @GenericGenerator(name = "uuid", strategy = "uuid2")
  7. @Column(name = "specialist_id",updatable = false)
  8. private String specialistId;
  9. // 其他字段...
  10. // 获取和设置方法
  11. }

正如您所看到的,这些实体具有不同的必填字段。
现在我想创建JWT,其中专家和顾客在端点上有不同的访问权限。
例如,专家在前端中具有不同的头文件,而与顾客不同。
现在我问您一个问题,如何将这两个不同的实体连接到JWT安全性?因为互联网上的所有教程/帖子只涉及一个用户实体,然后是单独的角色。

我尝试解决这个问题,但我遇到了如何替代用户实体并将其更改为我的专家和顾客实体:

  1. @Service
  2. public class CustomUserDetailsService implements UserDetailsService {
  3. @Autowired
  4. private UserRepository userRepository;
  5. @Override
  6. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  7. User user = userRepository.findByUsername(username);
  8. if(user == null) new UsernameNotFoundException("User not found");
  9. return user;
  10. }
  11. @Transactional
  12. public User loadUserById(Long id){
  13. User user = userRepository.getById(id);
  14. if(user == null) new UsernameNotFoundException("User not found");
  15. return user;
  16. }
  17. }
  1. @Component
  2. public class UserValidator implements Validator {
  3. @Override
  4. public boolean supports(Class<?> aClass) {
  5. return User.class.equals(aClass);
  6. }
  7. @Override
  8. public void validate(Object object, Errors errors) {
  9. User user = (User) object;
  10. if(user.getPassword().length() < 6){
  11. errors.rejectValue("password","Length","Password must be at least 6 characters");
  12. }
  13. if(!user.getPassword().equals(user.getConfirmPassword())){
  14. errors.rejectValue("confirmPassword","Match","Passwords must match");
  15. }
  16. }
  17. }
  1. @Component
  2. public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
  3. @Override
  4. public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
  5. InvalidLoginResponse loginResponse = new InvalidLoginResponse();
  6. String jsonLoginResponse = new Gson().toJson(loginResponse);
  7. httpServletResponse.setContentType("application/json");
  8. httpServletResponse.setStatus(401);
  9. httpServletResponse.getWriter().print(jsonLoginResponse);
  10. }
  11. }
  1. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  2. @Autowired
  3. private JwtTokenProvider tokenProvider;
  4. @Autowired
  5. private CustomUserDetailsService customUserDetailsService;
  6. // 其他方法...
  7. }
  1. @Component
  2. public class JwtTokenProvider {
  3. public String generateToken(Authentication authentication){
  4. User user = (User)authentication.getPrincipal();
  5. // 其他代码...
  6. }
  7. // 其他方法...
  8. }
  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(
  4. securedEnabled = true,
  5. jsr250Enabled = true,
  6. prePostEnabled = true
  7. )
  8. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  9. @Autowired
  10. private JwtAuthenticationEntryPoint unAuthorizedHandler;
  11. @Autowired
  12. private CustomUserDetailsService customUserDetailsService;
  13. @Autowired
  14. private BCryptPasswordEncoder bCryptPasswordEncoder;
  15. // 其他配置...
  16. }
英文:

I've got two entities as two type of account in my spring app:
Customer:

  1. @Entity
  2. @Table(name = &quot;customer&quot;)
  3. public class Customer {
  4. @Id
  5. @GeneratedValue(generator = &quot;uuid&quot;)
  6. @GenericGenerator(name = &quot;uuid&quot;, strategy = &quot;uuid2&quot;)
  7. @Column(name = &quot;customer_id&quot;,updatable = false)
  8. private String customerId;
  9. @NotBlank(message = &quot;Nickname may not be blank&quot;)
  10. @Size(min = 3, max = 20, message = &quot;Nickname &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  11. @Column(name = &quot;nickname&quot;,updatable = false)
  12. private String nickname;
  13. @NotBlank(message = &quot;City may not be blank&quot;)
  14. @Size(min = 3, max = 25, message = &quot;City &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  15. @Column(name = &quot;city&quot;)
  16. private String city;
  17. @NotBlank(message = &quot;Phone Number may not be blank&quot;)
  18. @Pattern(regexp=&quot;(^$|[0-9]{9})&quot;)
  19. @Column(name = &quot;phone_number&quot;,updatable = false)
  20. private String phoneNumber;
  21. @NotBlank(message = &quot;Email may not be blank&quot;)
  22. @Email
  23. @Column(name = &quot;mail&quot;,updatable = false)
  24. private String mail;
  25. @NotBlank(message = &quot;Password may not be blank&quot;)
  26. @Size(min = 5 , max = 30, message = &quot;Password &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  27. @Column(name = &quot;password&quot;)
  28. private String password;
  29. // getters setters
  30. }

and Specialist:

  1. @Entity
  2. @Table(name = &quot;specialist&quot;)
  3. public class Specialist {
  4. @Id
  5. @GeneratedValue(generator = &quot;uuid&quot;)
  6. @GenericGenerator(name = &quot;uuid&quot;, strategy = &quot;uuid2&quot;)
  7. @Column(name = &quot;specialist_id&quot;,updatable = false)
  8. private String specialistId;
  9. @NotBlank(message = &quot;Name may not be blank&quot;)
  10. @Size(min = 3, max = 20, message = &quot;Name &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  11. @Column(name = &quot;name&quot;,updatable = false)
  12. private String name;
  13. @NotBlank(message = &quot;Surname may not be blank&quot;)
  14. @Size(min = 3, max = 20, message = &quot;Name &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  15. @Column(name = &quot;surname&quot;,updatable = false)
  16. private String surname;
  17. @NotNull(message = &quot;Province may not be blank&quot;)
  18. @Column(name = &quot;province&quot;)
  19. private Province province;
  20. @NotBlank(message = &quot;City may not be blank&quot;)
  21. @Size(min = 3, max = 25, message = &quot;City &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  22. @Column(name = &quot;city&quot;)
  23. private String city;
  24. @NotBlank(message = &quot;Profession may not be blank&quot;)
  25. @Size(min = 3, max = 25, message = &quot;Profession &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  26. @Column(name = &quot;profession&quot;)
  27. private String profession;
  28. @NotBlank(message = &quot;Phone Number may not be blank&quot;)
  29. @Pattern(regexp=&quot;(^$|[0-9]{9})&quot;)
  30. @Column(name = &quot;phone_number&quot;,updatable = false)
  31. private String phoneNumber;
  32. @NotBlank(message = &quot;Email may not be blank&quot;)
  33. @Email
  34. @Column(name = &quot;mail&quot;,updatable = false)
  35. private String mail;
  36. @NotBlank(message = &quot;Password may not be blank&quot;)
  37. @Size(min = 5 , max = 30, message = &quot;Password &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
  38. @Column(name = &quot;password&quot;)
  39. private String password;
  40. @Column(name = &quot;rate&quot;)
  41. private HashMap&lt;String,Double&gt; rateStars;
  42. @Range(min = 0 , max = 5)
  43. @Column(name = &quot;average_rate&quot;)
  44. private Double averageRate;
  45. //getters setters
  46. }

As you can see this entities have got differently required fields.
Now I want to create JWT where specialist and customer have got other access to endpoints.
For example specialist has got other header than customer in front end.
And now i ask question to You, how connect two differently entities to JWT security? Because all of tutorials/posts in internet concerns JWT only to one User entity and then separate role.

I tried to solve this but I stand with this how to supersede User entity and change this to my Specialist and Customer entities:

  1. @Service
  2. public class CustomUserDetailsService implements UserDetailsService {
  3. @Autowired
  4. private UserRepository userRepository;
  5. @Override
  6. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  7. User user = userRepository.findByUsername(username);
  8. if(user == null) new UsernameNotFoundException(&quot;User not found&quot;);
  9. return user;
  10. }
  11. @Transactional
  12. public User loadUserById(Long id){
  13. User user = userRepository.getById(id);
  14. if(user == null) new UsernameNotFoundException(&quot;User not found&quot;);
  15. return user;
  16. }
  1. @Component
  2. public class UserValidator implements Validator {
  3. @Override
  4. public boolean supports(Class&lt;?&gt; aClass) {
  5. return User.class.equals(aClass);
  6. }
  7. @Override
  8. public void validate(Object object, Errors errors) {
  9. User user = (User) object;
  10. if(user.getPassword().length() &lt; 6){
  11. errors.rejectValue(&quot;password&quot;,&quot;Length&quot;,&quot;Password must be at least 6 characters&quot;);
  12. }
  13. if(!user.getPassword().equals(user.getConfirmPassword())){
  14. errors.rejectValue(&quot;confirmPassword&quot;,&quot;Match&quot;,&quot;Passwords must match&quot;);
  15. }
  16. }
  17. }
  1. @Component
  2. public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
  3. @Override
  4. public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
  5. InvalidLoginResponse loginResponse = new InvalidLoginResponse();
  6. String jsonLoginResponse = new Gson().toJson(loginResponse);
  7. httpServletResponse.setContentType(&quot;application/json&quot;);
  8. httpServletResponse.setStatus(401);
  9. httpServletResponse.getWriter().print(jsonLoginResponse);
  10. }
  11. }
  1. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  2. @Autowired
  3. private JwtTokenProvider tokenProvider;
  4. @Autowired
  5. private CustomUserDetailsService customUserDetailsService;
  6. @Override
  7. protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
  8. try{
  9. String jwt = getJWTFromRequest(httpServletRequest);
  10. if(StringUtils.hasText(jwt) &amp;&amp; tokenProvider.validateToken(jwt)){
  11. Long userId = tokenProvider.getUserIdFromJWT(jwt);
  12. User userDetails = customUserDetailsService.loadUserById(userId);
  13. UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
  14. userDetails,null, Collections.emptyList()
  15. );
  16. authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
  17. SecurityContextHolder.getContext().setAuthentication(authentication);
  18. }
  19. }catch (Exception ex){
  20. logger.error(&quot;Could not set user authentication in security context&quot;, ex);
  21. }
  22. filterChain.doFilter(httpServletRequest,httpServletResponse);
  23. }
  24. private String getJWTFromRequest(HttpServletRequest request) {
  25. String bearerToken = request.getHeader(HEADER_STRING);
  26. if(StringUtils.hasText(bearerToken) &amp;&amp; bearerToken.startsWith(TOKEN_PREFIX)){
  27. return bearerToken.substring(7, bearerToken.length());
  28. }
  29. return null;
  30. }
  31. }
  1. @Component
  2. public class JwtTokenProvider {
  3. public String generateToken(Authentication authentication){
  4. User user = (User)authentication.getPrincipal();
  5. Date now = new Date(System.currentTimeMillis());
  6. Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
  7. String userId = Long.toString(user.getId());
  8. Map&lt;String,Object&gt; claims = new HashMap&lt;&gt;();
  9. claims.put(&quot;id&quot;,(Long.toString(user.getId())));
  10. claims.put(&quot;username&quot;, user.getUsername());
  11. claims.put(&quot;fullName&quot;, user.getFullName());
  12. return Jwts.builder()
  13. .setSubject(userId)
  14. .setClaims(claims)
  15. .setIssuedAt(now)
  16. .setExpiration(expiryDate)
  17. .signWith(SignatureAlgorithm.HS512, SECRET)
  18. .compact();
  19. }
  20. public boolean validateToken(String token) {
  21. try{
  22. Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
  23. return true;
  24. }catch (SignatureException ex){
  25. System.out.println(&quot;Invalid JWT Signature&quot;);
  26. }catch (MalformedJwtException ex){
  27. System.out.println(&quot;Invalid JWT Token&quot;);
  28. }catch (ExpiredJwtException ex){
  29. System.out.println(&quot;Expired JWT token&quot;);
  30. }catch (UnsupportedJwtException ex){
  31. System.out.println(&quot;Unsupported JWT token&quot;);
  32. }catch (IllegalArgumentException ex){
  33. System.out.println(&quot;JWT claims string is empty&quot;);
  34. }
  35. return false;
  36. }
  37. public Long getUserIdFromJWT(String token){
  38. Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
  39. String id = (String)claims.get(&quot;id&quot;);
  40. return Long.parseLong(id);
  41. }
  42. }
  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(
  4. securedEnabled = true,
  5. jsr250Enabled = true,
  6. prePostEnabled = true
  7. )
  8. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  9. @Autowired
  10. private JwtAuthenticationEntryPoint unAuthorizedHandler;
  11. @Autowired
  12. private CustomUserDetailsService customUserDetailsService;
  13. @Autowired
  14. private BCryptPasswordEncoder bCryptPasswordEncoder;
  15. @Bean
  16. public JwtAuthenticationFilter jwtAuthenticationFilter(){
  17. return new JwtAuthenticationFilter();
  18. }
  19. @Override
  20. protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  21. authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
  22. }
  23. @Override
  24. @Bean(BeanIds.AUTHENTICATION_MANAGER)
  25. protected AuthenticationManager authenticationManager() throws Exception {
  26. return super.authenticationManager();
  27. }
  28. @Override
  29. protected void configure(HttpSecurity http) throws Exception {
  30. http.cors().and().csrf().disable()
  31. .exceptionHandling().authenticationEntryPoint(unAuthorizedHandler).and()
  32. .sessionManagement()
  33. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  34. .and()
  35. .headers().frameOptions().sameOrigin() //To enable H2 db
  36. .and()
  37. .authorizeRequests()
  38. .antMatchers(
  39. &quot;/&quot;,
  40. &quot;/favicon.ico&quot;,
  41. &quot;/**/*.png&quot;,
  42. &quot;/**/*.gif&quot;,
  43. &quot;/**/*.svg&quot;,
  44. &quot;/**/*.jpg&quot;,
  45. &quot;/**/*.html&quot;,
  46. &quot;/**/*.css&quot;,
  47. &quot;/**/*.js&quot;
  48. ).permitAll()
  49. .antMatchers(SIGN_UP_URLS).permitAll()
  50. .antMatchers(H2_URL).permitAll()
  51. .anyRequest().authenticated();
  52. http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  53. }
  54. }

答案1

得分: 2

所有在互联网上的教程/帖子只涉及将JWT令牌与单个用户实体关联,然后区分角色。

您可以在两个表中存储用户,但他们是两个不同角色的用户 - CustomerSpecialist

如何将这两个不同的实体连接到JWT安全性?

  1. @Override
  2. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  3. /**
  4. * 根据逻辑检查该用户名是否属于Customer/Specialist
  5. * 根据这一点创建一个新的权限 - ROLE_CUSTOMER或ROLE_SPECIALIST
  6. */
  7. }

Specialist在前端有与Customer不同的标头。

这些是菜单,如果我没记错的话。根据您使用上述方法传递的权限,您可以显示不同的菜单。

英文:

> all of tutorials/posts in internet concerns JWT only to one User entity and then separate role.

You may store users in two tables but they are users of two different roles- Customer and Specialist

>how connect two differently entities to JWT security?

  1. @Override
  2. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  3. /**
  4. * logic to check if this username belongs to Customer/Specialist
  5. * based on that create a new Authority - ROLE_CUSTOMER or ROLE_SPECIALIST
  6. */
  7. }

>specialist has got other header than customer in front end.

These are Menu if I am not wrong. Based on Authority that you passed using above method, you can show Menu.

huangapple
  • 本文由 发表于 2020年5月3日 20:19:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/61574328.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定