Авторизация спрингом

This commit is contained in:
Igor I
2025-02-07 19:44:35 +05:00
parent 76988693e4
commit cb710b2845
8 changed files with 271 additions and 45 deletions

View File

@ -0,0 +1,45 @@
package org.ccalm.jwt;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.util.Collections;
import java.util.UUID;
import org.ccalm.jwt.models.ErrorResponseModel;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<ErrorResponseModel> handleNotFound(NoHandlerFoundException ex) {
ErrorResponseModel errorResponse = new ErrorResponseModel(
10000 + HttpStatus.NOT_FOUND.value(),
"Not_Found",
UUID.randomUUID().toString()
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponseModel> handleException(Exception ex) {
ErrorResponseModel errorResponse = new ErrorResponseModel(
10000 + HttpStatus.NOT_FOUND.value(),
"Internal_Server_Error", //Collections.singletonList("Internal_Server_Error"),
UUID.randomUUID().toString()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
@RequestMapping("/error")
public ResponseEntity<ErrorResponseModel> handleError() {
ErrorResponseModel errorResponse = new ErrorResponseModel(
10000 + HttpStatus.NOT_FOUND.value(),
"Unknown_error",
UUID.randomUUID().toString()
);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
}

View File

@ -39,6 +39,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.commons.text.RandomStringGenerator;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.context.ApplicationContext;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@ -61,11 +63,14 @@ import java.util.regex.Pattern;
import java.io.InputStream;
import java.util.Properties;
import java.io.IOException;
import java.util.stream.Collectors;
import org.springframework.security.core.Authentication;
@Controller
public class MainController implements ServletContextAware {
private static final Logger logger = LogManager.getLogger(MainController.class);
@Value("${spring.application.name}")
String application_name = "";
@Value("${issuer.name}")
@ -135,19 +140,6 @@ public class MainController implements ServletContextAware {
return json.toString();
}
//---------------------------------------------------------------------------
public JSONObject createJSONError(int code, String message, String setting, String marker) {
JSONObject json = new JSONObject();
try {
json.put("error_code", code);
json.put("error_message", Arrays.asList(message));
json.put("error_setting", Arrays.asList(setting));
json.put("error_marker", marker);
} catch (JSONException e) {
logger.error(e);
}
return json;
}
//---------------------------------------------------------------------------
public String createHTMLError(int code, String message) {
return "<!DOCTYPE html><html><head><meta http-equiv=\"refresh\" content=\"0; url='" + this.url_main + "?msg=" + message + "'\" /></head><body></body></html>";
}
@ -293,6 +285,18 @@ public class MainController implements ServletContextAware {
json.put("name",application_name);
//json.put("active_connections",dataSource.getHikariPoolMXBean().getActiveConnections());
//json.put("idle_connections",dataSource.getHikariPoolMXBean().getIdleConnections());
// Вывод всех зарегистрированных маршрутов в системе
ApplicationContext context = SpringContext.getApplicationContext();
if (context != null) {
RequestMappingHandlerMapping mapping = context.getBean(RequestMappingHandlerMapping.class);
Set<String> endpoints = mapping.getHandlerMethods().keySet().stream()
.map(info -> info.toString())
.collect(Collectors.toSet());
System.out.println("=== Registered API endpoints ===");
endpoints.forEach(System.out::println);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
@ -305,7 +309,7 @@ public class MainController implements ServletContextAware {
public Object get_settings(@CookieValue(value = "jwt_a", defaultValue = "") String jwt_a, @RequestParam(required=false,name="lng",defaultValue = "1") String language_id) {
Translation trt = new Translation(language_id,jdbcTemplate);
try{
if(jwt_a.equals("") || countOccurrences(jwt_a, '.')!=2)
if(jwt_a.isEmpty() || countOccurrences(jwt_a, '.')!=2)
{
throw new CustomException(10000, trt.trt("Please_log_in"),null);
}
@ -348,15 +352,10 @@ public class MainController implements ServletContextAware {
@Operation(summary = "Create or update user settings", description = "")
@RequestMapping(value = "/set_settings",method = {RequestMethod.POST,RequestMethod.GET},produces = "application/json;charset=utf-8")
@ResponseBody
public String set_settings(SettingModel setting, @CookieValue(value = "jwt_a", defaultValue = "") String jwt_a, @RequestParam(required=false,name="lng",defaultValue = "1") String language_id) {
public ResponseEntity<Object> set_settings(SettingModel setting, @CookieValue(value = "jwt_a", defaultValue = "") String jwt_a, @RequestParam(required=false,name="lng",defaultValue = "1") String language_id) {
Translation trt = new Translation(language_id,jdbcTemplate);
JSONObject json = new JSONObject();
try{
json.put("error_code",0);
//json.put("error_message","");
//json.put("error_marker",(String)null);
if(jwt_a.equals("") || countOccurrences(jwt_a, '.')!=2)
if(jwt_a.isEmpty() || countOccurrences(jwt_a, '.')!=2)
{
throw new CustomException(10000, trt.trt("Please_log_in"),null);
}
@ -405,14 +404,17 @@ public class MainController implements ServletContextAware {
parameters.addValue("value", setting.getValue());
jdbcTemplate.query(sql, parameters, new DBTools.JsonRowMapper());
return new ResponseEntity<>(new ErrorResponseModel(0), HttpStatus.OK);
} catch (CustomException e) {
json = e.getJson();
String uuid = UUID.randomUUID().toString();
logger.error(uuid, e);
return new ResponseEntity<>(e.getErrorResponseModel(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"),(String)null, uuid);
logger.error(uuid, e);
return new ResponseEntity<>(new ErrorResponseModel(10000, trt.trt("Internal_Server_Error"), null, uuid), HttpStatus.INTERNAL_SERVER_ERROR);
}
return json.toString();
}
//------------------------------------------------------------------------------------------------------------------
@Operation(
@ -447,7 +449,7 @@ public class MainController implements ServletContextAware {
) {
Translation trt = new Translation(language_id, jdbcTemplate);
try {
if (jwt_a.equals("") || countOccurrences(jwt_a, '.') != 2) {
if (jwt_a.isEmpty() || countOccurrences(jwt_a, '.') != 2) {
return new ResponseEntity<>(new ErrorResponseModel(10000, trt.trt("Please_log_in"), null, null), HttpStatus.INTERNAL_SERVER_ERROR);
}
@ -542,7 +544,7 @@ public class MainController implements ServletContextAware {
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
}
return json.toString();
}
@ -677,7 +679,7 @@ public class MainController implements ServletContextAware {
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
}
return json.toString();
}
@ -692,7 +694,7 @@ public class MainController implements ServletContextAware {
json.put("error_code",0);
json.put("error_message","");
if(jwt_a.equals("") || countOccurrences(jwt_a, '.')!=2)
if(jwt_a.isEmpty() || countOccurrences(jwt_a, '.')!=2)
{
throw new CustomException(10000, trt.trt("Please_log_in"),null);
}
@ -744,7 +746,7 @@ public class MainController implements ServletContextAware {
catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
} finally {
//try { if(conn!=null) conn.close(); } catch (SQLException e) { throw new RuntimeException(e); }
}
@ -1021,7 +1023,7 @@ public class MainController implements ServletContextAware {
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
} finally {
//try { if(conn!=null) conn.close(); } catch (SQLException e) { throw new RuntimeException(e); }
}
@ -1223,7 +1225,7 @@ public class MainController implements ServletContextAware {
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
} finally {
//try { if(conn!=null) conn.close(); } catch (SQLException e) { throw new RuntimeException(e); }
}
@ -1253,7 +1255,7 @@ public class MainController implements ServletContextAware {
json.put("error_code",0);
json.put("error_message","");
if(jwt_a.equals("") || countOccurrences(jwt_a, '.')!=2 || jwt_r.equals("") || countOccurrences(jwt_r, '.')!=2 )
if(jwt_a.isEmpty() || countOccurrences(jwt_a, '.')!=2 || jwt_r.isEmpty() || countOccurrences(jwt_r, '.')!=2 )
{
logout(response,request);
throw new CustomException(10000, trt.trt("Please_log_in"),null);
@ -1334,7 +1336,7 @@ public class MainController implements ServletContextAware {
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid,e);
json = createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
json = Tools.createJSONError(10000,trt.trt("Internal_Server_Error"), (String)null, uuid);
} finally {
}
@ -1508,13 +1510,13 @@ public class MainController implements ServletContextAware {
if(update==null)
throw new CustomException(10000,trt.trt("Please_send_a_valid_JSON_string_in_your_request"),null);
if(update.getLogin().equals(""))
if(update.getLogin().isEmpty())
throw new CustomException(10000,trt.trt("The_login_field_is_empty"),null);
if (!Tools.isValidEmail(update.getLogin()))
throw new CustomException(10000, trt.trt("The_email_field_is_incorrect"),null);
if(update.getPassword().equals(""))
if(update.getPassword().isEmpty())
throw new CustomException(10000,trt.trt("The_password_field_is_empty"),null);
if(update.getPasswordNew().equals(""))
if(update.getPasswordNew().isEmpty())
throw new CustomException(10000,trt.trt("The_new_password_field_is_empty"),null);
if(!Pattern.compile("[0-9]").matcher(update.getPasswordNew()).find())
@ -1619,10 +1621,15 @@ public class MainController implements ServletContextAware {
)
@RequestMapping(value = "/alive",method = {RequestMethod.POST,RequestMethod.GET},produces = "application/json;charset=utf-8")
@ResponseBody
public ResponseEntity<Object> alive(HttpServletResponse response,HttpServletRequest request, @CookieValue(value = "jwt_a", defaultValue = "") String jwt_a, @CookieValue(value = "lng",defaultValue="1") String language_id) {
//public ResponseEntity<Object> alive(HttpServletResponse response,HttpServletRequest request, @CookieValue(value = "jwt_a", defaultValue = "") String jwt_a, @CookieValue(value = "lng",defaultValue="1") String language_id) {
public ResponseEntity<Object> alive(HttpServletResponse response,HttpServletRequest request, Authentication authentication, @CookieValue(value = "lng",defaultValue="1") String language_id) {
Translation trt = new Translation(language_id,jdbcTemplate);
try {
if(jwt_a.equals("") || countOccurrences(jwt_a, '.')!=2)
++++++++++++
String username = authentication.getName();
return ResponseEntity.ok("User: " + username);
/*if(jwt_a.isEmpty() || countOccurrences(jwt_a, '.')!=2)
{
throw new CustomException(10000, Collections.singletonList(trt.trt("Please_log_in")),null);
}
@ -1635,7 +1642,7 @@ public class MainController implements ServletContextAware {
.parseClaimsJws(jwt_a);
} catch (Exception e) {
throw new CustomException(10000, Arrays.asList(trt.trt("Please_log_in"), trt.trt("JWT_token_verification_error")),null);
}
}*/
//If this is a repeat authorization, then we inform the client about it
String result=null;
try(Cache cache = new Cache(redis_host,redis_port,redis_password)) {
@ -1651,11 +1658,12 @@ public class MainController implements ServletContextAware {
}
}
return new ResponseEntity<>(new ErrorResponseModel(0), HttpStatus.OK);
} catch (CustomException e) {
return new ResponseEntity<>(e.getErrorResponseModel(), HttpStatus.OK);
} catch (Exception e) {
String uuid = UUID.randomUUID().toString();
logger.error(uuid, e);
return new ResponseEntity<>(new ErrorResponseModel(10000, trt.trt("Internal_Server_Error"), null, uuid), HttpStatus.INTERNAL_SERVER_ERROR);
} finally {
}
}
}

View File

@ -0,0 +1,113 @@
package org.ccalm.jwt;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.function.Consumer;
import java.util.List;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private static final Logger logger = LogManager.getLogger(SecurityConfig.class);
@Value("${public.key}")
String public_key;
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 🔹 Закомментировал проверку JWT, чтобы отключить авторизацию
/*
String jwt_a = null;
if (request.getCookies() != null) {
for (var cookie : request.getCookies()) {
if ("jwt_a".equals(cookie.getName())) {
jwt_a = cookie.getValue();
break;
}
}
}
if (jwt_a == null || jwt_a.isEmpty()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
try {
Jws<Claims> claims = Jwts.parserBuilder()
.setSigningKey(getPublicKey())
.build()
.parseClaimsJws(jwt_a);
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Token");
return;
}
*/
// Создаём фиктивного пользователя с ролью "USER"
var authorities = List.of(new SimpleGrantedAuthority("ROLE_USER"));
var authentication = new UsernamePasswordAuthenticationToken("testUser", null, authorities);
// Устанавливаем пользователя в SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authentication);
// Пропускаем дальше
filterChain.doFilter(request, response);
}
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthFilter jwtAuthFilter) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
//http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()); // Отключил защиту, теперь все запросы разрешены
http.authorizeHttpRequests(auth -> auth
//.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").authenticated() // Swagger доступен только после авторизации
.requestMatchers("/login", "/create").permitAll() // Логин и регистрация - доступны без авторизации
//.requestMatchers("/admin/**").hasRole("ADMIN") // Все пути, начинающиеся с /admin/, доступны только админам
.anyRequest().authenticated() // Все остальные запросы требуют авторизации
);
http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
private PublicKey getPublicKey() {
try {
byte[] keyBytes = Base64.getDecoder().decode(this.public_key);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
} catch (Exception e) {
logger.error(e);
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package org.ccalm.jwt;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}

View File

@ -1,6 +1,9 @@
package org.ccalm.jwt;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ccalm.jwt.tools.DBTools;
import org.ccalm.jwt.tools.Storage;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
@ -9,6 +12,9 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import java.util.List;
public class Translation {
private static final Logger logger = LogManager.getLogger(Translation.class);
public int language_id;
public NamedParameterJdbcTemplate jdbcTemplate;
Translation(String lng, NamedParameterJdbcTemplate jdbcTemplate){

View File

@ -24,3 +24,4 @@ public class UserModel {
@JsonProperty("password")
private String password;
}

View File

@ -2,6 +2,9 @@ package org.ccalm.jwt.tools;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ccalm.jwt.MainController;
import org.json.JSONException;
import org.json.JSONObject;
@ -10,12 +13,28 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Tools {
//---------------------------------------------------------------------------
private static final Logger logger = LogManager.getLogger(Tools.class);
//---------------------------------------------------------------------------
public static JSONObject createJSONError(int code, String message, String setting, String marker) {
JSONObject json = new JSONObject();
try {
json.put("error_code", code);
json.put("error_message", Arrays.asList(message));
json.put("error_setting", Arrays.asList(setting));
json.put("error_marker", marker);
} catch (JSONException e) {
logger.error(e);
}
return json;
}
//---------------------------------------------------------------------------
//Зашифровать
public static String encryptText(String pass,String data){
String encryptedBase64="";