Já tenho mecanismo de Primavera cookie de segurança no lugar para a minha candidatura, agora apenas para os da API, eu preciso adicionar mecanismo de autenticação baseada em tokens JWT. Estou usando MultiHttpSecurityConfiguration do Spring Security com dois classe aninhada.
Se ambos sessão e JWT mecanismo de token devem ser incluídos juntos em um aplicativo ou não é uma questão completamente diferente, eu preciso conseguir duas coisas.
- autenticação baseada em sessão do Spring Security com o bolinho vai funcionar como era antes.
- Necessidade de adicionar um cabeçalho de autenticação para a API do
package com.leadwinner.sms.config;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.leadwinner.sms.CustomAuthenticationSuccessHandler;
import com.leadwinner.sms.CustomLogoutSuccessHandler;
import com.leadwinner.sms.config.jwt.JwtAuthenticationProvider;
import com.leadwinner.sms.config.jwt.JwtAuthenticationTokenFilter;
import com.leadwinner.sms.config.jwt.JwtSuccessHandler;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@ComponentScan(basePackages = "com.leadwinner.sms")
public class MultiHttpSecurityConfig {
@Autowired
@Qualifier("userServiceImpl")
private UserDetailsService userServiceImpl;
@Autowired
private JwtAuthenticationProvider authenticationProvider;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
@Configuration
@Order(1)
public static class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtauthFilter;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/web/umgmt/**").authorizeRequests()
.antMatchers("/web/umgmt/**").authenticated()
.and()
.addFilterBefore(jwtauthFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@Configuration
@Order(2)
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Bean
public CustomAuthenticationEntryPoint getBasicAuthEntryPoint() {
return new CustomAuthenticationEntryPoint();
}
@Override
public void configure(HttpSecurity http) throws Exception {
logger.info("http configure");
http
.antMatcher("/**").authorizeRequests()
.antMatchers("/login/authenticate").permitAll()
.antMatchers("/resources/js/**").permitAll()
.antMatchers("/resources/css/**").permitAll()
.antMatchers("/resources/images/**").permitAll()
.antMatchers("/web/initial/setup/**").permitAll()
.antMatchers("/dsinput/**").permitAll().antMatchers("/dsoutput/**").permitAll()
.and()
.formLogin()
.loginPage("/login").usernameParameter("employeeId").passwordParameter("password")
.successForwardUrl("/dashboard")
.defaultSuccessUrl("/dashboard", true)
.successHandler(customAuthenticationSuccessHandler())
.failureForwardUrl("/logout")
.loginProcessingUrl("/j_spring_security_check")
.and().logout()
.logoutSuccessUrl("/logout").logoutUrl("/j_spring_security_logout")
.logoutSuccessHandler(customLogoutSuccessHandler())
.permitAll()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.and().sessionManagement()
.sessionFixation().none()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.invalidSessionUrl("/logout")
.and().exceptionHandling().accessDeniedPage("/logout").and().csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
@Bean
public AuthenticationSuccessHandler customAuthenticationSuccessHandler() {
return new CustomAuthenticationSuccessHandler();
}
@Bean
public LogoutSuccessHandler customLogoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
}
}
JwtAuthenticationTokenFilter.java
package com.leadwinner.sms.config.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
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)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
username, null, null);
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);
}
}
JwtTokenUtil.java
package com.leadwinner.sms.config.jwt;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@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));
}
}
Parece que agora o filtro JwtSecurityConfig não está sendo aplicada para o caminho que mencionei. Qualquer ajuda será apreciada.
Já li esta pergunta. I seguido o mesmo.
Spring Security com Spring Bota: Mix autenticação básica com autenticação por token JWT
Edit: Adicionado JwtAuthenticationTokenFilter, JwtTokenUtil
Eu tenho sua exigência.
- Você precisa expor da API que deve ser acessados através JWT token no cabeçalho de solicitação (para cada pedido).
- E também aplicação web deve ser assegurado através de mecanismo de autenticação baseada em formulário que deve funcionar com base em http sessão.
Você pode conseguir isso por dois filtros de autenticação.
Filtro - 1 : for Rest API (JwtAuthTokenFilter), que deve ser apátrida e identificados por autorização enviados token no pedido de cada vez.
Filter - 2 : Você precisa de um outro filtro (UsernamePasswordAuthenticationFilter) por mola de segurança padrão fornece isso se você configurá-lo http.formLogin()
. Aqui cada pedido é identificado pela sessão ( JSESSIONID
cookie) associado. Se o pedido não contém sessão válida em seguida, ele será redirecionado para a autenticação de entrada pontos (dizer: login-page).
api-url-pattern = "/api/**" [strictly for @order(1)]
webApp-url-pattern = "/**" [ wild card "/**" always used for higer order otherwise next order configuration becomes dead configuration]
Abordagem
Definir classe configuração principal com
@EnableWebSecurity
Criar duas classes estáticas internas que se deve estender
WebSecurityConfigurerAdapter
e anotados com @Configuration e @Order. Aqui ordem para a configuração api resto deve ser 1 e para a aplicação web a fim de configuração deve ser superior a 1Consulte minha resposta neste link para obter mais detalhes que tem explicação em profundidade com código necessário. Sinta-se livre para pedir link para download a partir do repositório github, se necessário.
Limitação
Aqui ambos os filtros vai trabalhar lado a lado (Parellally). Quero dizer da aplicação web, mesmo que se um usuário é autenticado por sessão, ele não pode acessar a API do sem um símbolo JWT.
EDIT
Para exigência do OP, onde ele não quer para definir qualquer papel, mas acesso à API é permitido para o usuário autenticado. Por sua exigência modificado abaixo configuração.
http.csrf().disable()
.antMatcher("/web/umgmt/**").authorizeRequests()
.antMatcher("/web/umgmt/**").authenticated() // use this