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

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

Create JWT security for two entities

问题

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

@Entity
@Table(name = "customer")
public class Customer {

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(name = "customer_id",updatable = false)
    private String customerId;
    
    // 其他字段...
    
    // 获取和设置方法
}

专家:

@Entity
@Table(name = "specialist")
public class Specialist {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(name = "specialist_id",updatable = false)
    private String specialistId;
    
    // 其他字段...
    
    // 获取和设置方法
}

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

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

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if(user == null) new UsernameNotFoundException("User not found");
        return user;
    }

    @Transactional
    public User loadUserById(Long id){
        User user = userRepository.getById(id);
        if(user == null) new UsernameNotFoundException("User not found");
        return user;
    }
}
@Component
public class UserValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass) {
        return User.class.equals(aClass);
    }

    @Override
    public void validate(Object object, Errors errors) {

        User user = (User) object;

        if(user.getPassword().length() < 6){
            errors.rejectValue("password","Length","Password must be at least 6 characters");
        }

        if(!user.getPassword().equals(user.getConfirmPassword())){
            errors.rejectValue("confirmPassword","Match","Passwords must match");
        }
    }
}
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

        InvalidLoginResponse loginResponse = new InvalidLoginResponse();
        String jsonLoginResponse = new Gson().toJson(loginResponse);

        httpServletResponse.setContentType("application/json");
        httpServletResponse.setStatus(401);
        httpServletResponse.getWriter().print(jsonLoginResponse);
    }
}
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    // 其他方法...

}
@Component
public class JwtTokenProvider {

    public String generateToken(Authentication authentication){
        User user = (User)authentication.getPrincipal();
        
        // 其他代码...
    }

    // 其他方法...
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint unAuthorizedHandler;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    // 其他配置...
}
英文:

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

@Entity
@Table(name = &quot;customer&quot;)
public class Customer {

    @Id
    @GeneratedValue(generator = &quot;uuid&quot;)
    @GenericGenerator(name = &quot;uuid&quot;, strategy = &quot;uuid2&quot;)
    @Column(name = &quot;customer_id&quot;,updatable = false)
    private String customerId;
    @NotBlank(message = &quot;Nickname may not be blank&quot;)
    @Size(min = 3, max = 20, message = &quot;Nickname &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;nickname&quot;,updatable = false)
    private String nickname;
    @NotBlank(message = &quot;City may not be blank&quot;)
    @Size(min = 3, max = 25, message = &quot;City &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;city&quot;)
    private String city;
    @NotBlank(message = &quot;Phone Number may not be blank&quot;)
    @Pattern(regexp=&quot;(^$|[0-9]{9})&quot;)
    @Column(name = &quot;phone_number&quot;,updatable = false)
    private String phoneNumber;
    @NotBlank(message = &quot;Email may not be blank&quot;)
    @Email
    @Column(name = &quot;mail&quot;,updatable = false)
    private String mail;
    @NotBlank(message = &quot;Password may not be blank&quot;)
    @Size(min = 5 , max = 30, message = &quot;Password &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;password&quot;)
    private String password;

// getters setters
}

and Specialist:

@Entity
@Table(name = &quot;specialist&quot;)
public class Specialist {
    @Id
    @GeneratedValue(generator = &quot;uuid&quot;)
    @GenericGenerator(name = &quot;uuid&quot;, strategy = &quot;uuid2&quot;)
    @Column(name = &quot;specialist_id&quot;,updatable = false)
    private String specialistId;
    @NotBlank(message = &quot;Name may not be blank&quot;)
    @Size(min = 3, max = 20, message = &quot;Name &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;name&quot;,updatable = false)
    private String name;
    @NotBlank(message = &quot;Surname may not be blank&quot;)
    @Size(min = 3, max = 20, message = &quot;Name &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;surname&quot;,updatable = false)
    private String surname;
    @NotNull(message = &quot;Province may not be blank&quot;)
    @Column(name = &quot;province&quot;)
    private Province province;
    @NotBlank(message = &quot;City may not be blank&quot;)
    @Size(min = 3, max = 25, message = &quot;City &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;city&quot;)
    private String city;
    @NotBlank(message = &quot;Profession may not be blank&quot;)
    @Size(min = 3, max = 25, message = &quot;Profession &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;profession&quot;)
    private String profession;
    @NotBlank(message = &quot;Phone Number may not be blank&quot;)
    @Pattern(regexp=&quot;(^$|[0-9]{9})&quot;)
    @Column(name = &quot;phone_number&quot;,updatable = false)
    private String phoneNumber;
    @NotBlank(message = &quot;Email may not be blank&quot;)
    @Email
    @Column(name = &quot;mail&quot;,updatable = false)
    private String mail;
    @NotBlank(message = &quot;Password may not be blank&quot;)
    @Size(min = 5 , max = 30, message = &quot;Password &#39;${validatedValue}&#39; isn&#39;t correct =&gt; must be between {min} and {max} characters&quot;)
    @Column(name = &quot;password&quot;)
    private String password;
    @Column(name = &quot;rate&quot;)
    private HashMap&lt;String,Double&gt; rateStars;
    @Range(min = 0 , max = 5)
    @Column(name = &quot;average_rate&quot;)
    private Double averageRate;
//getters setters
}

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:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if(user == null) new UsernameNotFoundException(&quot;User not found&quot;);
        return user;
    }

    @Transactional
    public User loadUserById(Long id){
        User user = userRepository.getById(id);
        if(user == null) new UsernameNotFoundException(&quot;User not found&quot;);
        return user;
    }
@Component
public class UserValidator implements Validator {

    @Override
    public boolean supports(Class&lt;?&gt; aClass) {
        return User.class.equals(aClass);
    }

    @Override
    public void validate(Object object, Errors errors) {

        User user = (User) object;

        if(user.getPassword().length() &lt; 6){
            errors.rejectValue(&quot;password&quot;,&quot;Length&quot;,&quot;Password must be at least 6 characters&quot;);
        }

        if(!user.getPassword().equals(user.getConfirmPassword())){
            errors.rejectValue(&quot;confirmPassword&quot;,&quot;Match&quot;,&quot;Passwords must match&quot;);
        }

    }
}
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

        InvalidLoginResponse loginResponse = new InvalidLoginResponse();
        String jsonLoginResponse = new Gson().toJson(loginResponse);

        httpServletResponse.setContentType(&quot;application/json&quot;);
        httpServletResponse.setStatus(401);
        httpServletResponse.getWriter().print(jsonLoginResponse);

    }
}
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        try{

            String jwt = getJWTFromRequest(httpServletRequest);

            if(StringUtils.hasText(jwt) &amp;&amp; tokenProvider.validateToken(jwt)){
                Long userId = tokenProvider.getUserIdFromJWT(jwt);
                User userDetails = customUserDetailsService.loadUserById(userId);

                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails,null, Collections.emptyList()
                );

                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }

        }catch (Exception ex){
            logger.error(&quot;Could not set user authentication in security context&quot;, ex);
        }

        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    private String getJWTFromRequest(HttpServletRequest request) {

        String bearerToken = request.getHeader(HEADER_STRING);

        if(StringUtils.hasText(bearerToken) &amp;&amp; bearerToken.startsWith(TOKEN_PREFIX)){
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;

    }


}
@Component
public class JwtTokenProvider {

    public String generateToken(Authentication authentication){
        User user = (User)authentication.getPrincipal();
        Date now = new Date(System.currentTimeMillis());

        Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);

        String userId = Long.toString(user.getId());

        Map&lt;String,Object&gt; claims = new HashMap&lt;&gt;();
        claims.put(&quot;id&quot;,(Long.toString(user.getId())));
        claims.put(&quot;username&quot;, user.getUsername());
        claims.put(&quot;fullName&quot;, user.getFullName());

        return Jwts.builder()
                .setSubject(userId)
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();

    }


    public boolean validateToken(String token) {

        try{
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
            return true;
        }catch (SignatureException ex){
            System.out.println(&quot;Invalid JWT Signature&quot;);
        }catch (MalformedJwtException ex){
            System.out.println(&quot;Invalid JWT Token&quot;);
        }catch (ExpiredJwtException ex){
            System.out.println(&quot;Expired JWT token&quot;);
        }catch (UnsupportedJwtException ex){
            System.out.println(&quot;Unsupported JWT token&quot;);
        }catch (IllegalArgumentException ex){
            System.out.println(&quot;JWT claims string is empty&quot;);
        }
        return false;
    }

    public Long getUserIdFromJWT(String token){
        Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        String id = (String)claims.get(&quot;id&quot;);

        return Long.parseLong(id);
    }

}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint unAuthorizedHandler;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter(){
        return new JwtAuthenticationFilter();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unAuthorizedHandler).and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .headers().frameOptions().sameOrigin() //To enable H2 db
                .and()
                .authorizeRequests()
                .antMatchers(
                        &quot;/&quot;,
                        &quot;/favicon.ico&quot;,
                        &quot;/**/*.png&quot;,
                        &quot;/**/*.gif&quot;,
                        &quot;/**/*.svg&quot;,
                        &quot;/**/*.jpg&quot;,
                        &quot;/**/*.html&quot;,
                        &quot;/**/*.css&quot;,
                        &quot;/**/*.js&quot;
                ).permitAll()
                .antMatchers(SIGN_UP_URLS).permitAll()
                .antMatchers(H2_URL).permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

答案1

得分: 2

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

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

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

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

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?

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

>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:

确定