Контекст: Я пишу веб-приложение, предназначенное для обслуживания двух разных API, каждый со своим фильтром аутентификации.Оба фильтра обрабатывают и аутентифицируют токены JWT, однако сами токены содержат разные полезные данные и поступают из разных источников аутентификации.Кроме того, несмотря на то, что токены различаются и поступают из разных источников, они имеют одни и те же учетные данные для входа в систему и защищены одним и тем же ключом.
Т.е.: вы можете перейти к любому из этих URL-адресов авторизации., с теми же учетными данными и верните другой токен JWT с совершенно разными полезными нагрузками.
/ auth / auth1 / login / auth / auth2 / login
Проблема: Проблема, с которой я сталкиваюсь, заключается в том, что если я аутентифицируюсь на одном из них, у меня есть доступ к другому.Даже без предоставления токена.
То есть, если я перехожу к / запросы / что-то, аутентифицируюсь с использованием токена-носителя от провайдера аутентификации 1, затем перехожу к / ims / oneroster / v1p1 / кое-что (без передачи другого токенаот провайдера аутентификации 2) Я могу получить доступ к данным, даже если я не проходил аутентификацию, используя фильтр, связанный с этим путем.
На данный момент я знаю единственный способ убедиться, что каждый фильтрЧтобы правильно проверить, является ли токен пользователя действительным, нужно поместить SecurityContextHolder.clearContext (); в верхнюю часть каждого из фильтров doFilterInternal.Однако я почти уверен, что не должен этого делать.
Может кто-нибудь увидеть проблему с тем, что у меня ниже, или дать какие-нибудь рекомендации?
SecurityConfig.class
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
//https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity
@Configuration @Order(1)
public static class XPressWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private final CacheService cacheService;
public XPressWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/requests/**")
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilter(new JWTAuthorizationFilter(authenticationManagerBean(), cacheService))
//.exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
;
}
}
@Configuration @Order(2)
public static class OneRosterWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
private final CacheService cacheService;
public OneRosterWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/ims/oneroster/v1p1/**")
.authorizeRequests().anyRequest().authenticated()
.and().addFilter(new OneRosterAuthorizationFilter(authenticationManagerBean(), cacheService))
;
}
}
}
OneRosterAuthorizationFilter.class
public class OneRosterAuthorizationFilter extends BasicAuthenticationFilter {
private final CacheService cacheService;
public OneRosterAuthorizationFilter(AuthenticationManager authManager, CacheService cacheService) {
super(authManager);
this.cacheService = cacheService;
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
logger.debug("GOING TO ONEROSTER FILTER");
AuthRequest authRequest = new AuthRequest(req);
if(authRequest.isAuthEnabled()) {
if(authRequest.isHeader() || (authRequest.isParameter() && authRequest.isAllowTokenParameter())) {
UsernamePasswordAuthenticationToken authentication = getAuthentication(req, authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req, AuthRequest authRequest) {
if(StringUtils.isBlank(authRequest.getToken())) {
return null; //Token was blank... 403 Forbidden
}
DecodedToken decodedToken = TokenDecoder.decodeToken(authRequest.getToken());
Application application = null;
if(decodedToken != null) {
application = new Application(decodedToken.getAppId(), decodedToken.getToken(), cacheService);
}
try {
if(!System.getenv("provider_id").equalsIgnoreCase(decodedToken.getProviderId())) {
throw new JWTVerificationException("Provider Ids Don't Match....");
}
if(application != null && StringUtils.isNotBlank(application.getApp().getProviderSecret())) {
JWT.require(Algorithm.HMAC256(application.getApp().getProviderSecret().getBytes()))
.withIssuer(PropertiesLoader.getInstance().getProperty("security.auth.jwt.issuer"))
.build().verify(authRequest.getToken());
return new UsernamePasswordAuthenticationToken(application, decodedToken.getToken(), getACLs(application));
}
}
catch (JWTVerificationException exception) {
//https://medium.com/fullstackblog/spring-security-jwt-token-expired-custom-response-b85437914b81
req.setAttribute("JWTVerificationException", exception.getMessage());
return null;
}
return null; //DecodedToken or Application was null... 403 Forbidden
}
private Collection<GrantedAuthority> getACLs(Application application) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
application.getPermissions().forEach(pathPermission -> {
if(pathPermission.getGet()) {
grantedAuthorities.add(new SimpleGrantedAuthority("get:" + pathPermission.getPath()));
}
if(pathPermission.getPost()) {
grantedAuthorities.add(new SimpleGrantedAuthority("post:" + pathPermission.getPath()));
}
if(pathPermission.getPut()) {
grantedAuthorities.add(new SimpleGrantedAuthority("put:" + pathPermission.getPath()));
}
if(pathPermission.getDelete()) {
grantedAuthorities.add(new SimpleGrantedAuthority("delete:" + pathPermission.getPath()));
}
});
return grantedAuthorities;
}
}
JWTAuthorizationFilter.class
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private final CacheService cacheService;
public JWTAuthorizationFilter(AuthenticationManager authManager, CacheService cacheService) {
super(authManager);
this.cacheService = cacheService;
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
logger.debug("GOING TO JWT FILTER");
AuthRequest authRequest = new AuthRequest(req);
if(authRequest.isAuthEnabled()) {
if(authRequest.isHeader() || (authRequest.isParameter() && authRequest.isAllowTokenParameter())) {
UsernamePasswordAuthenticationToken authentication = getAuthentication(req, authRequest);
if(authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req, AuthRequest authRequest) {
if(StringUtils.isBlank(authRequest.getToken())) {
return null; //Token was blank... 403 Forbidden
}
DecodedToken decodedToken = TokenDecoder.decodeToken(authRequest.getToken());
Application application = null;
if(decodedToken != null) {
application = new Application(decodedToken.getApplication_id(), decodedToken.getToken(), cacheService);
}
try {
if(application != null && StringUtils.isNotBlank(application.getApp().getProviderSecret())) {
JWT.require(Algorithm.HMAC256(application.getApp().getProviderSecret().getBytes()))
.withIssuer(PropertiesLoader.getInstance().getProperty("security.auth.jwt.issuer"))
.build().verify(authRequest.getToken());
return new UsernamePasswordAuthenticationToken(application, decodedToken.getToken(), getACLs(application));
}
}
catch (JWTVerificationException exception) {
//https://medium.com/fullstackblog/spring-security-jwt-token-expired-custom-response-b85437914b81
req.setAttribute("JWTVerificationException", exception.getMessage());
return null;
}
return null; //DecodedToken or Application was null... 403 Forbidden
}
private Collection<GrantedAuthority> getACLs(Application application) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
application.getPermissions().forEach(pathPermission -> {
if(pathPermission.getGet()) {
grantedAuthorities.add(new SimpleGrantedAuthority("get:" + pathPermission.getPath()));
}
if(pathPermission.getPost()) {
grantedAuthorities.add(new SimpleGrantedAuthority("post:" + pathPermission.getPath()));
}
if(pathPermission.getPut()) {
grantedAuthorities.add(new SimpleGrantedAuthority("put:" + pathPermission.getPath()));
}
if(pathPermission.getDelete()) {
grantedAuthorities.add(new SimpleGrantedAuthority("delete:" + pathPermission.getPath()));
}
});
return grantedAuthorities;
}
}
Note: Application is the class that implements UserDetails