| Version 3 (modified by , 25 hours ago) ( diff ) |
|---|
Други Развојни Активности
Анализа на перформанси
Извештај за најпопуларни курсеви според оценка
SQL:
SELECT
c.id AS course_id,
ct.title_short AS course_title,
cv.version_number AS course_version,
cv.active AS is_active,
COUNT(DISTINCT e.id) AS total_enrollments,
SUM(r.rating) / COUNT(r.id) AS average_rating,
COUNT(r.id) AS total_reviews
FROM course c
JOIN course_translate ct ON c.id = ct.course_id
JOIN course_version cv ON c.id = cv.course_id
JOIN enrollment e ON cv.id = e.course_version_id
JOIN review r ON e.id = r.enrollment_id
WHERE ct.language = :language
GROUP BY c.id, ct.id, cv.id
ORDER BY SUM(r.rating) / COUNT(r.id) DESC
Индекси:
1. course_translate - Unique (course_id, language)
CREATE UNIQUE INDEX uk_course_translate_course_language ON course_translate(course_id, language);
Користење: JOIN на ct.course_id + WHERE филтер на ct.language
Подобрување: Index Scan наместо Seq Scan (~60x побрзо)
---
2. course_version - Index (course_id)
CREATE INDEX idx_course_version_course_id ON course_version(course_id);
Користење: JOIN на cv.course_id
Подобрување: Директен lookup (~45x побрзо)
---
3. enrollment - Index (course_version_id)
CREATE INDEX idx_enrollment_course_version_id ON enrollment(course_version_id);
Користење: JOIN на e.course_version_id
Подобрување: Избегнува full table scan (~60x побрзо)
---
4. review - Unique (enrollment_id)
CREATE UNIQUE INDEX uk_review_enrollment ON review(enrollment_id);
Користење: JOIN на r.enrollment_id
Подобрување: Unique scan (~35x побрзо)
---
5. course - Primary Key (id)
-- Автоматски креиран со PRIMARY KEY CREATE UNIQUE INDEX course_pkey ON course(id);
Користење: Примарна табела PK lookup
---
Сумарно:
- Без индекси: Seq Scan на сите табели (бавно)
- Со индекси: Index Scan на сите JOIN-ови и WHERE (брзо)
- Подобрување: ~30x побрзо
Безбедност и заштита
JWT Token Authorization (Spring Security)
JWT (JSON Web Token) e stateless начин на автентикација - server НЕ чува информации за активни сесии во база, туку сите потребни податоци се во самиот token кој корисникот го чува локално/cookie. JWT содржи енкодирана json структура на информации (user_id, email, role, expiry...). {{{!nosql @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(request -> request
.requestMatchers("/api/verification-tokens/").permitAll() .requestMatchers("/api/auth/").permitAll() .requestMatchers("/api/courses/").permitAll() .requestMatchers("/api/test/").permitAll() .requestMatchers("/api/auth/oauth2/").permitAll() .requestMatchers("/oauth2/", "/login/oauth2/").permitAll() .anyRequest().authenticated()
) .exceptionHandling(exception -> exception
.authenticationEntryPoint(authenticationEntryPoint)
) .oauth2Login(oauth2 -> oauth2
Use the custom handler instead of defaultSuccessUrl() .successHandler(oauth2SuccessHandler) .failureUrl("http://localhost:5173/login?error")
) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}}}
Хеширање на пасворди (BCrypt)
Пасвордите на корисниците и експертите се чуваат во база во хеширана форма преку BCrypt, а не како plain text. Ова овозможува сигурно чување на пасвордите.
SQL Injection Prevention (Spring JPA/JPQL)
Преку JPA/JPQL се спречува SQL Injection напад каде корисникот внесува злонамерен код за да манипулира со базата на податоци. Нападите се избегнуваат преку третирање на параметарот како plain data, а не команда.
Безбедно: {{{!nosql
@Query("select u.email from User u where u.id = :userId") String getUserEmailById(@Param("userId") Long userId);
}}}
Небезбедно: {{{!nosql
String query = "SELECT * FROM users WHERE email = '" + email + "'";
}}}
CORS Configuration
CORS е безбеден механизам за заштита од requests од различни домени. Со тоа се заштитуваме од можни злонамерни requests. {{{!nosql @Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("http://localhost:*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); config.setAllowCredentials(true); config.addExposedHeader(HttpHeaders.CONTENT_DISPOSITION);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/", config); return source;
}
}}}
