Я делаю REST API в Spring Boot, и мне нужно его защитить. После некоторых поисков я нашел несколько статей и учебных пособий, в которых пытались объяснить, как создать безопасный API с использованием JWT и OAUTH2 для аутентификации приложения, которое потребляет ресурсы API и пользователей, использующих эти приложения, но я не очень понялну, потому что я впервые это увидел.
Я реализовал код, который выполняет аутентификацию JWT, но для работы некоторых конечных точек должен быть разрешен доступ из любого места, и это очень плохо, я думаю,Когда я пытался реализовать аутентификацию OAUTH2, чтобы исправить это, это не сработало.
Код для реализации аутентификации JWT выглядит так: Извините, если это слишком много кода, но, как я уже сказал, я не уверен, какэто работает, и мне нужно понять.
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -7858869558953243875L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
org.springframework.security.core.AuthenticationException authException)
throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "token");
}
}
@Component
public class JWTRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtill jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("token") == null ? request.getParameter("token") : request.getHeader("token");
String username = null;
String jwtToken = null;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, token, Content-Type, Authorization, credential, X-XSRF-TOKEN, ");
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Não foi possível obter o token JWT");
} catch (ExpiredJwtException e) {
System.out.println("O token JWT expirou");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
@Component
public class JwtTokenUtill implements Serializable {
private static final long serialVersionUID = -2550185165626007488L;
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
@Value("${jwt.secret}")
private String secret;
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateTokenCliente(Cliente cliente) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, cliente.getEmail(), cliente.getIdCliente());
}
public String generateTokenProfissional(Profissional profissional) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, profissional.getEmail(), profissional.getIdProfissional());
}
private String doGenerateToken(Map<String, Object> claims, String subject, Long id) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
@Service
public class JwtUserDetailsService implements UserDetailsService {
@Autowired
ClienteDTORepository clienteRepository;
@Autowired
ProfissionalDTORepository profissionalRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
ClienteDTO cliente = clienteRepository.findByEmail(email);
ProfissionalDTO profissional = profissionalRepository.findByEmail(email);
if (cliente != null) {
User user = new User(cliente.getEmail(), cliente.getIdCliente().toString(), new ArrayList<>());
return user;
} else if (profissional != null) {
User user = new User(profissional.getEmail(), profissional.getIdProfissional().toString(), new ArrayList<>());
return user;
}
throw new UsernameNotFoundException("Usuário com e-mail "+email+" não foi encontrado.");
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JWTRequestFilter jwtRequestFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("kelvin").password("123").roles("ADMIN");
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests(
.antMatchers(HttpMethod.POST, "/auth/**").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
public class JWTRequest implements Serializable {
private static final long serialVersionUID = 5926468583005150707L;
private String email;
private String senha;
public JWTRequest() {
}
public JWTRequest(String email, String senha) {
this.setEmail(email);
this.setSenha(senha);
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSenha() {
return this.senha;
}
public void setSenha(String senha) {
this.senha = senha;
}
}
public class JWTResponse implements Serializable {
private static final long serialVersionUID = -8091879091924046844L;
private final String jwttoken;
public JWTResponse(String jwttoken) {
this.jwttoken = jwttoken;
}
public String getToken() {
return this.jwttoken;
}
}
@RestController
@CrossOrigin(origins = "http://localhost:3000")
@SupportedOptions(value = { "eventBusIndex", "verbose" })
public class JwtAuthenticationResource {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtill jwtTokenUtil;
@Autowired
private ClienteRepository clienteRepository;
@Autowired
private ProfissionalRepository profissionalRepository;
@PostMapping("/login/cliente")
public ResponseEntity<?> createAuthenticationTokenCliente(@RequestBody JWTRequest authenticationRequest)
throws Exception {
final Cliente cliente = clienteRepository.findUserLogin(
authenticationRequest.getEmail(),
authenticationRequest.getSenha());
if (cliente != null) {
final String token = jwtTokenUtil.generateTokenCliente(cliente);
return ResponseEntity.ok(new JWTResponse(token));
}
return ResponseEntity.ok("{\"error\": \"Usuario não cadastrado\"}");
}
@PostMapping("/login/profissional")
public ResponseEntity<?> createAuthenticationTokenProfissional(@RequestBody JWTRequest authenticationRequest)
throws Exception {
final Profissional profissional = profissionalRepository.findUserLogin(
authenticationRequest.getEmail(),
authenticationRequest.getSenha());
if (profissional != null) {
final String token = jwtTokenUtil.generateTokenProfissional(profissional);
return ResponseEntity.ok(new JWTResponse(token));
}
return ResponseEntity.ok("{\"error\": \"Usuario não cadastrado\"}");
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
Классы Profissional и Cliente, которые появляются в коде, являютсяпользователи из моего приложения.
С этим кодом работает реализация JWT, но я не могу реализовать OAUTH2 для аутентификации приложения, использующего API.
Когда я пытался сделатьOAUTH2 работает, ничего не произошло, JWT продолжал работать, и OAUTH2 не дал никаких признаков того, что он был настроен, даже с зависимостями String Secutiry.