Обратите внимание: предоставленная вами информация / код недостаточны. Поскольку вы не предоставили коды X509AuthenticationServer
и JwtRequestFilter
, я не могу сказать, что именно не так.
Предполагая, что это ваше срочное требование, пытающееся объяснить все возможные варианты.
Как это работает сейчас - любой запрос запускает addFilterBefore и отклоняется по причине отсутствия токена jwt.
Да, он пропустит бросок фильтра для любого запроса. Единственная вещь, которую вы должны позаботиться в фильтре, это проверить заголовок, если он есть, затем перейти к аутентификации JWT, иначе пропустите аутентификацию JWT ( Проверьте код класса моего фильтра, если блок ).
Поскольку ваша весенняя защита настроена на x509, объект аутентификации будет установлен субъектом сертификата с использованием регулярного выражения .x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)")
Для X509 субъект запроса будет от субъекта сертификата клиента
Для запроса JWT, поскольку он состоит из сертификата сервера, принципал будет установлен из субъекта сертификата сервера. Теперь переопределите принципал, получив имя пользователя из токена JWT и предоставив права доступа, необходимые для этого пользователя.
Ниже заданного субъекта сертификата сервера и принципала, полученного от регулярного выражения, является "Правин" Subject: EMAILADDRESS=nlpraveennl@gmail.com, CN=Praveen, OU=Answer, O=StackOverflow, L=BENGALURU, ST=KA, C=IN
Ниже приведены данные сертификата клиента и посмотрите на предмет. Принципал, полученный от регулярных выражений: "Веданта" [
Version: V1
Subject: EMAILADDRESS=vedanta@gmail.com, CN=vedanta, OU=some unit, O=some comapany, L=San diego, ST=CA, C=US
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 4096 bits
modulus: 817322829240490927539679977649457347113079769252522527800627995161015683461174014845370356721299781132807751160825513223084246773438383264091041820195243162665509891042158368380019167357193944418022840037166629645037683573001749034046239097004884878919482801656531508586406926436161114752390080130151471441072696492233900694526232243678291240183028778626646828176141484812179759739175161094296327915519918417719601837669497535100237474334129104633049344560273440876841464270970566837970617275659841740418106346304917775719024288908219661239022501256531355020518204348890248534705892780384737192506755315365883201062844995260628679776392945804218936346471987181753817158168697446990117525268883019172878686864407803654159029932574084328051385395658876802073285425506794524283532870369321962543974623256690683454729681498079311854836252232196330777070325603711372859419866719120909302184446204160932841096818392866335724975192880990513954025684813917171551040959488266330875554906980729293764667616531866907648957912796417658137011975409928985274876102141544954375822680116494691313951508398067207453068155470604238471192479465455519868221517071480577973522583550201343549400093003081375335896091143428006322327934212827941149315676683
public exponent: 65537
Validity: [From: Tue Oct 15 09:04:58 IST 2019,
To: Fri Oct 09 09:04:58 IST 2020]
Issuer: EMAILADDRESS=nlpraveennl@gmail.com, CN=Praveen, OU=Answer, O=StackOverflow, L=BENGALURU, ST=KA, C=IN
SerialNumber: [ baafa655 dd56be52]
]
Что еще может быть не так
1. Для API версии 2 (JWT) : Вы не отправляете сертификат сервера в своем запросе. Я согласен, ему не нужен клиентский сертификат, но сервер принимает связь только через SSL-соединение.
Вы не можете проверить его с помощью curl cmd. Это работает, только если ваш SSL подписан CA.
Вы не можете проверить это в Google Chrome, так как Google Chrome также нуждается в SSL, подписанном CA.
Взломать для тестирования:
Использовать сертификат сервера и ключ безопасности вместе с вашим токеном JWT, как указано ниже
curl -ik --cert server.crt --key serverPrivateKey.pem -H "Authorization:Bearer jwtToken" "https://localhost:8443/v2/api/yourpath"
Это должно работать.
2. для API, отличных от v2 (без JWT) : Вы не включаете сертификат клиента в запрос. Правильный способ проверить это.
curl -ik --cert pavel.crt --key myPrivateKey.pem "https://localhost:8443/v1/hello"
curl -ik --cert pavel.crt --key myPrivateKey.pem "https://localhost:8443/v1.5/hello"
Я проверил с моей стороны, он работает для меня без каких-либо проблем. Единственное, что у меня есть
К вашему сведению, добавив сюда код.
конфигурация @Configuration
@EnableWebSecurity
public class SpringSecurityConfig
{
@Configuration
@Order(1)
public static class JwtConfiguration extends WebSecurityConfigurerAdapter
{
@Autowired
private JwtAuthenticationTokenFilter jwtRequestFilter;
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.antMatcher("/v2/**").authorizeRequests()
.antMatchers("/v2/**").authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable();
}
}
@Configuration
@Order(4)
public static class X509Configuration extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().anyRequest().authenticated()
.and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService())
.and().exceptionHandling().accessDeniedPage("/forbidden");
}
@Bean
public UserDetailsService userDetailsService()
{
return new UserDetailsService()
{
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
System.out.println("[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]");
System.out.println(username);
System.out.println("[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]");
return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
};
}
}
}
JWT-фильтр токенов аутентификации @Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException
{
System.out.println("(((((((((((((((())))))))))))))");
final String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer "))
{
String authToken = header.substring(7);
System.out.println(authToken);
try
{
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null)
{
// here username should be validated with database and get authorities from database if valid
// Say just to hard code sending same username received
if (jwtTokenUtil.validateToken(authToken, username))
{
List<GrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority("ROLE_APIUSER"));
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, authList);
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
}
catch (Exception e)
{
System.out.println("Unable to get JWT Token, possibly expired");
}
}
chain.doFilter(request, response);
}
}
Класс JwtTokenUtility @Component
public class JwtTokenUtil implements Serializable
{
private static final long serialVersionUID = 8544329907338151549L;
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
private String secret = "my-secret";
public String getUsernameFromToken(String token)
{
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token)
{
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver)
{
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token)
{
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token)
{
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(String username)
{
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, username);
}
private String doGenerateToken(Map<String, Object> claims, String subject)
{
return "Bearer "+Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, String usernameFromToken)
{
final String username = getUsernameFromToken(token);
return (username.equals(usernameFromToken) && !isTokenExpired(token));
}
//To generate token for testing
public static void main(String[] args)
{
JwtTokenUtil tu = new JwtTokenUtil();
String s1 = tu.generateToken("hello");
System.out.println(s1);
String user = tu.getUsernameFromToken(s1);
System.out.println(user);
}
}
Контроллер / API @RestController
public class HelloController
{
//Client certificate
@RequestMapping(path = "/v1/hello")
public String helloV1()
{
return "HELLO Version 1 - "+((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
}
//Client certificate
@RequestMapping(path = "/v1.5/hello")
public String helloV1Dot5()
{
return "HELLO Version 1.5 - "+((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
}
//Jwt
@RequestMapping(path = "/v2/hello")
public String helloV2()
{
return "HELLO Version 2 - "+SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}