英文:
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 = "customer")
public class Customer {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
@Column(name = "customer_id",updatable = false)
private String customerId;
@NotBlank(message = "Nickname may not be blank")
@Size(min = 3, max = 20, message = "Nickname '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "nickname",updatable = false)
private String nickname;
@NotBlank(message = "City may not be blank")
@Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "city")
private String city;
@NotBlank(message = "Phone Number may not be blank")
@Pattern(regexp="(^$|[0-9]{9})")
@Column(name = "phone_number",updatable = false)
private String phoneNumber;
@NotBlank(message = "Email may not be blank")
@Email
@Column(name = "mail",updatable = false)
private String mail;
@NotBlank(message = "Password may not be blank")
@Size(min = 5 , max = 30, message = "Password '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "password")
private String password;
// getters setters
}
and Specialist:
@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;
@NotBlank(message = "Name may not be blank")
@Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "name",updatable = false)
private String name;
@NotBlank(message = "Surname may not be blank")
@Size(min = 3, max = 20, message = "Name '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "surname",updatable = false)
private String surname;
@NotNull(message = "Province may not be blank")
@Column(name = "province")
private Province province;
@NotBlank(message = "City may not be blank")
@Size(min = 3, max = 25, message = "City '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "city")
private String city;
@NotBlank(message = "Profession may not be blank")
@Size(min = 3, max = 25, message = "Profession '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "profession")
private String profession;
@NotBlank(message = "Phone Number may not be blank")
@Pattern(regexp="(^$|[0-9]{9})")
@Column(name = "phone_number",updatable = false)
private String phoneNumber;
@NotBlank(message = "Email may not be blank")
@Email
@Column(name = "mail",updatable = false)
private String mail;
@NotBlank(message = "Password may not be blank")
@Size(min = 5 , max = 30, message = "Password '${validatedValue}' isn't correct => must be between {min} and {max} characters")
@Column(name = "password")
private String password;
@Column(name = "rate")
private HashMap<String,Double> rateStars;
@Range(min = 0 , max = 5)
@Column(name = "average_rate")
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("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;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try{
String jwt = getJWTFromRequest(httpServletRequest);
if(StringUtils.hasText(jwt) && 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("Could not set user authentication in security context", ex);
}
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
private String getJWTFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader(HEADER_STRING);
if(StringUtils.hasText(bearerToken) && 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<String,Object> claims = new HashMap<>();
claims.put("id",(Long.toString(user.getId())));
claims.put("username", user.getUsername());
claims.put("fullName", 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("Invalid JWT Signature");
}catch (MalformedJwtException ex){
System.out.println("Invalid JWT Token");
}catch (ExpiredJwtException ex){
System.out.println("Expired JWT token");
}catch (UnsupportedJwtException ex){
System.out.println("Unsupported JWT token");
}catch (IllegalArgumentException ex){
System.out.println("JWT claims string is empty");
}
return false;
}
public Long getUserIdFromJWT(String token){
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
String id = (String)claims.get("id");
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(
"/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers(SIGN_UP_URLS).permitAll()
.antMatchers(H2_URL).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
答案1
得分: 2
所有在互联网上的教程/帖子只涉及将JWT令牌与单个用户实体关联,然后区分角色。
您可以在两个表中存储用户,但他们是两个不同角色的用户 - Customer
和 Specialist
。
如何将这两个不同的实体连接到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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论