У меня есть приложение с Spring boot backend и React front-end. однако я хочу обеспечить его безопасностью Spring и JWT
, когда мой интерфейсный пользователь пытается загрузить запрещенные (несанкционированные) страницы, даже после получения токена. Он пытается загрузить некоторые «куски». Вот некоторые из моих классов безопасности и реагирования:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
@Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
@Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
@Value("${jwt.get.token.uri}")
private String authenticationPath;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
}
@Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/" //Other Stuff You want to Ignore
)
.and()
.ignoring()
.antMatchers("/h2-console/**/**");//Should not be in Production!
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
ResourceResolver resolver = new ReactResourceResolver();
registry.addResourceHandler("/**")
.resourceChain(true)
.addResolver(resolver);
}
public class ReactResourceResolver implements ResourceResolver {
private static final String REACT_DIR = "/static/";
private static final String REACT_STATIC_DIR = "static";
private Resource index = new ClassPathResource(REACT_DIR + "index.html");
private List<String> rootStaticFiles = Arrays.asList("favicon.ico",
"asset-manifest.json", "manifest.json", "service-worker.js");
@Override
public Resource resolveResource(HttpServletRequest request, String requestPath,
List<? extends Resource> locations, ResourceResolverChain chain) {
return resolve(requestPath, locations);
}
@Override
public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain) {
Resource resolvedResource = resolve(resourcePath, locations);
if (resolvedResource == null) {
return null;
}
try {
return resolvedResource.getURL().toString();
} catch (IOException e) {
return resolvedResource.getFilename();
}
}
private Resource resolve(String requestPath, List<? extends Resource> locations) {
if (requestPath == null) return null;
if (rootStaticFiles.contains(requestPath)
|| requestPath.startsWith(REACT_STATIC_DIR)) {
return new ClassPathResource(REACT_DIR + requestPath);
} else
return index;
}
}
}
И контроллер:
@RestController
@CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" })
public class JwtAuthenticationRestController {
@Value("Authorization")
private String tokenHeader;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService jwtInMemoryUserDetailsService;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtTokenRequest authenticationRequest)
throws AuthenticationException {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtTokenResponse(token));
}
@RequestMapping(value = "/refresh", method = RequestMethod.GET)
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
String authToken = request.getHeader(tokenHeader);
final String token = authToken.substring(7);
String username = jwtTokenUtil.getUsernameFromToken(token);
JwtUserDetails user = (JwtUserDetails) jwtInMemoryUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.canTokenBeRefreshed(token)) {
String refreshedToken = jwtTokenUtil.refreshToken(token);
return ResponseEntity.ok(new JwtTokenResponse(refreshedToken));
} else {
return ResponseEntity.badRequest().body(null);
}
}
@ExceptionHandler({ AuthenticationException.class })
public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
private void authenticate(String username, String password) {
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new AuthenticationException("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new AuthenticationException("INVALID_CREDENTIALS", e);
}
}
}
Также UserDetails и UserDetailsService
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder bcryptEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserModel user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(),
Arrays.stream(user.getRole().split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
}
public UserModel save(UserModel user) {
UserModel newUser = new UserModel();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.encode(user.getPassword()));
newUser.setRole("USER_ROLE");
return userRepository.save(newUser);
}
}
public class JwtUserDetails implements UserDetails {
private static final long serialVersionUID = 5155720064139820502L;
private final String username;
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
public JwtUserDetails(String username, String password, String role) {
this.username = username;
this.password = password;
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
this.authorities = authorities;
}
...getters/setters
}
Класс модели пользователя:
public class UserModel {
String username;
String password;
String role;
}
...
И я использую базу данных Oracle.