есть ли возможность, что я могу использовать оба типа аутентификации в одном проекте.
Да, вы можете. Наличие двух фильтров обработки аутентификации.
Фильтр - 1 : для API покоя (JwtAuthTokenFilter), который должен быть без состояний и идентифицироваться токеном авторизации, отправляемым в запросе каждый раз.
Фильтр - 2 : вам нужен другой фильтр (UsernamePasswordAuthenticationFilter). По умолчанию spring-security обеспечивает это, если вы настраиваете его с помощью http.formLogin()
. Здесь каждый запрос идентифицируется связанным сеансом (JSESSIONID
cookie). Если запрос не содержит допустимого сеанса, он будет перенаправлен на точку входа аутентификации (скажем, login-page).
Рекомендуемый шаблон URL api-url-pattern = "/api/**"
webApp-url-pattern = "/**"
Как это работает URL с /api/**
будут передаваться через JwtAuthTokenFilter
, где он будет читать токен и, если он имеет действительный токен, устанавливает объект аутентификации ицепочка продолжается. Если у него нет действительного запроса, цепочка будет разорвана, и ответ будет отправлен со статусом 401 (неавторизовано).
URL, отличные от /api/**
, будут обработаны UsernamePasswordAuthenticationFilter
[который по умолчанию в весенней безопасности настроен конфигурацией .formLogin()
. Он проверит допустимый сеанс, если он не содержит действительный сеанс, он перенаправит на настроенный logoutSuccessUrl.
Примечание: Ваше Webapp не может получить доступ к API через существующий сеанс. Какой вариант у вас есть, это использовать токен Jwt для доступа к API из веб-приложения.
Как настроить Чтобы получить два разных фильтра обработки аутентификации, вам следуетнастроить несколько настроек безопасности http с другим порядком
Конфигурацию защиты нескольких http можно настроить, объявив статические классы в своем классе конфигурации безопасности, как указано ниже.
(Несмотря на то, что OP запросил концепцию, разумно представить код мудрым. может помочь вам для справки)
Конфигурация безопасности Spring @Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.gmail.nlpraveennl")
public class SpringSecurityConfig
{
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
@Configuration
@Order(1)
public static class RestApiSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private JwtAuthenticationTokenFilter jwtauthFilter;
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").hasAnyRole("APIUSER")
.and()
.addFilterBefore(jwtauthFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Configuration
@Order(2)
public static class LoginFormSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
{
auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin@123#")).roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.antMatcher("/**").authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin();
http.sessionManagement().maximumSessions(1).expiredUrl("/customlogin?expired=true");
}
}
}
Фильтр токенов аутентификации JWT @Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException
{
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)
{
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);
}
}
Класс утилиты токена JWT @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));
}
}
Конфигурация сервлета диспетчера @Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.gmail.nlpraveennl") //Do not skip componentscan
public class ServletConfiguration implements WebMvcConfigurer
{
@Bean
public ViewResolver configureViewResolver()
{
InternalResourceViewResolver viewResolve = new InternalResourceViewResolver();
viewResolve.setPrefix("/WEB-INF/jsp/");
viewResolve.setSuffix(".jsp");
return viewResolve;
}
@Bean
public ResourceBundleMessageSource messageSource()
{
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}