platform-auth-lib
Описание
Библиотека для авторизации пользователя
Инструкция для подключения
Добавить в pom.xml проекта следующие строчки. Вместо Tag добавить версию последнего релиза
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.vladdossik</groupId>
<artifactId>platform-auth-lib</artifactId>
<version>v.0.0.9</version>
</dependency>
и если отстутствует security, то его тоже нужно будет добавить
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
В application.yml добавить следующие настройки
auth-lib:
web:
auth:
baseUrl: ${AUTH_SERVICE_URL}
responseTimeout: 1000
maxBodySizeForLog: 512
В docker-compose.yml добавить
AUTH_SERVICE_URL: http://auth-service:8081
Затем в классе приложения (class SomeSpringApplication) прописать ComponentScan
@ComponentScan(basePackages = "lissalearning")
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
Создать UserDetailsServiceImpl и добавить метод loadUserByToken
@Transactional
public UserDetailsImpl loadUserByToken(HttpServletRequest httpServletRequest) throws ApiClientException {
return authClient.getUserDetails(httpServletRequest);
}
Создать AuthFilter и переопределить метод doFilterInternal
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String requestPath = request.getServletPath();
if (isSwaggerRequest(requestPath) || requestPath.contains("internal")) {
filterChain.doFilter(request, response);
return;
}
try {
UserDetailsImpl userDetails = userDetailsService.loadUserByToken(request);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
resolver.resolveException(request, response, null, e);
}
filterChain.doFilter(request, response);
}
private boolean isSwaggerRequest(String requestPath) {
return requestPath.startsWith("/swagger-ui") || requestPath.startsWith("/v3/api-docs");
}
Создать SecurityConfiguration, пробросить фильтр и userDetailsService
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
private final UserDetailsServiceImpl userDetailsService;
@Bean
public AuthFilter authorizationFilter() {
return new AuthFilter();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.authenticationProvider(authenticationProvider());
http.addFilterBefore(authorizationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Если нужно добавить дополнительные поля у пользователя, то нужно создать класс, который имплементирует интерфейс UserDetails и методом build, который принимает в себя UserDetailsImpl user из либы
public class UserAuthenticationDetails implements UserDetails {
private static final long serialVersionUID = 1L;
private UUID id; // дополнительное поле
private String username;
private UUID externalId;
private Collection<? extends GrantedAuthority> authorities;
public UserAuthenticationDetails(UUID id, String username,
Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.authorities = authorities;
}
public static UserAuthenticationDetails build(UserDetailsImpl user, UUID externalId) {
return new UserAuthenticationDetails(
externalId,
user.getUsername(),
user.getAuthorities());
}
//getters, setters and equals
}
Затем в контроллерах добавить PreAuthorize, где нужно по необходимости прикрыть нужными ролями
Пример из user-service, UserController class
Данные могут получить админ, модератор или сам пользователь
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_MODERATOR') or authentication.principal.getId() == #externalId")
@GetMapping("/{externalId}")
@Operation(summary = "Получить пользователя по id")
public UserResponseDto getUserById(@PathVariable UUID externalId) {
return userService.getUserById(externalId);
}
Также не забыть добавить в Swagger возможность прокидывать Authorization header
@Bean
public OpenAPI getOpenApi() {
return new OpenAPI()
.info(
new Info()
.title("Api")
.description(
"Description")
).addSecurityItem(
new SecurityRequirement()
.addList("Bearer Authentication"))
.components(
new Components()
.addSecuritySchemes("Bearer Authentication", createAPIKeyScheme()));
}
private SecurityScheme createAPIKeyScheme() {
return new SecurityScheme().type(SecurityScheme.Type.HTTP)
.bearerFormat("JWT")
.scheme("bearer");
}
Если необходимо получить данные авторизированного пользователя из контекста:
AuthenticationContextHolder.getUserInfo()...