У меня есть два типа учетных записей в моем весеннем приложении: Клиент:
@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
}
и Специалист:
@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
}
Как вы можете видеть, эти объекты получили по-разному обязательные поля. Теперь я хочу создать JWT, где у специалиста и клиента есть другой доступ к конечным точкам. Например, у специалиста есть другой заголовок, а не клиент в передней части. А теперь я задаю вам вопрос, как соединить две разные сущности с безопасностью JWT? Поскольку все учебные пособия / сообщения в inte rnet касаются 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;
@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);
}
}