У меня есть веб-приложение, которое использует среду Spring вместе с Spring MVC для создания конечных точек REST. Аутентификация обеспечивается контейнером с использованием безопасности Java EE (в частности, области безопасности в Wildfly 9, поддерживаемой Active Directory), а роли загружаются из базы данных приложений. У меня есть обертка запроса, примененная фильтром, чтобы сделать данные пользователя доступными через обычный API сервлета.Использование JSR-250 (@RolesAllowed) аннотации Spring MVC и проверка контейнера
web.xml:
<web-app ... version="3.1">
<security-constraint>
<web-resource-collection>
<web-resource-name>Unauthenticated Resources</web-resource-name>
<url-pattern>/favicon.ico</url-pattern>
<url-pattern>/NoAccess.html</url-pattern>
<url-pattern>/login</url-pattern>
<url-pattern>/css/*</url-pattern>
<url-pattern>/font/*</url-pattern>
<url-pattern>/images/*</url-pattern>
<url-pattern>/js/*</url-pattern>
<url-pattern>/lib/*</url-pattern>
<url-pattern>/partials/*</url-pattern>
</web-resource-collection>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>All Resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>App Realm</realm-name>
<form-login-config>
<form-login-page>/Login.jsp</form-login-page>
<form-error-page>/NoAccess.html</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>*</role-name>
</security-role>
</web-app>
SecurityFilter.java
@WebFilter(urlPatterns = { "*" })
public class SecurityFilter implements Filter {
...
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Don't wrap unauthenticated requests
if (httpRequest.getRemoteUser() == null) {
chain.doFilter(httpRequest, httpResponse);
return;
}
Person user = this.personRepository.findByLogin(httpRequest.getRemoteUser());
if (user != null) {
List<String> roles = this.personRepository.getRoles(user.getId());
HttpServletRequest wrappedRequest = new RequestWrapper(httpRequest, user, roles);
chain.doFilter(wrappedRequest, httpResponse);
} else {
httpResponse.sendRedirect(httpRequest.getContextPath() + "/NoAccess.html");
}
}
}
Authentication работает и внутри контроллера, я могу сделать request.getUserPrincipal()
и request.isUserInRole("SYSTEM_ADMIN")
и они правильно вернуться. Теперь я хочу добавить авторизацию в приложение. Я хочу применить правила авторизации, используя аннотацию JSR-250 @RolesPermitted
по моим методам контроллера; Я не хочу перечислять все правила в отдельном файле/классе конфигурации. Пример из контроллера:
@RestController
@RequestMapping("/people")
public class PersonController {
...
@RolesAllowed({Role.SYSTEM_ADMIN, Role.DEPARTMENT_ADMIN, Role.SYSTEM_AUDITOR, Role.AUDITOR})
@RequestMapping
public List<Person> listPeople(HttpServletRequest request) {
return this.personRepository.getPeople();
}
}
Конечно, это ничего не делает сам по себе, поэтому я добавил класс конфигурации с @EnableWebSecurity
и @EnableGlobalMethodSecurity(jsr250Enabled = true)
. Теперь, когда я ударил этот ресурс, я получил ошибку org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
. Достаточно справедливо, поэтому я следую примеру https://gist.github.com/virgium03/86938e43219bc28632d0, но я все равно получаю ту же ошибку. Кажется, фильтр никогда не добавляется в цепочку фильтров.
Я читал много учебников и сообщений, но все они в значительной степени говорят одно и то же и не работают для меня. Должно быть что-то простое, что мне не хватает - в конце концов, вся информация доступна для Spring Security для принятия решений о авторизации. Это возможно (вероятно?) Я пропустил что-то простое здесь. Вся помощь очень ценится!
Редактировать
Я добавил следующий код в мой SecurityFilter
класс для заполнения контекста Spring безопасности. Он работает, но я чувствую, что это не идеальное решение.
List<GrantedAuthority> authorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
PreAuthenticatedAuthenticationToken auth = new PreAuthenticatedAuthenticationToken(user, "", authorities);
auth.setDetails(new WebAuthenticationDetails(wrappedRequest));
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
Но, глядя на источник J2eePreAuthenticatedProcessingFilter, он показывает, что он создает объект аутентификации из HttpRequest и помещает его в контекст безопасности (через AbstractPreAuthenticatedProcessingFilter). Разве это не все, что мне нужно? – Doughnuts
Я посмотрел на источник J2eePreAuthenticatedProcessingFilter и не создал объект аутентификации. Он просто возвращает пользователя-пользователя из HttpRequest. –
См. Https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java (суперкласс J2eePreAuthenticatedProcessingFilter) – Doughnuts