Roh*_*ari 2 java spring spring-mvc spring-security jwt
我有一种情况,我正在使用 Spring MVC(jsp、控制器、服务、dao)和基于会话的身份验证。但是现在我将几个 url 用作用于集成目的的 RESTful Web 服务。
仅对于那些请求,我需要使用基于令牌(例如 JWT)的身份验证。
那么,我是否有可能在同一个项目中使用两种类型的身份验证。
有没有可能我可以在同一个项目中使用两种类型的身份验证。
是的你可以。通过具有两个身份验证处理过滤器。
过滤器 - 1:用于 Rest API (JwtAuthTokenFilter),它应该是无状态的,并由每次请求中发送的授权令牌标识。
过滤器 - 2:您需要另一个过滤器 (UsernamePasswordAuthenticationFilter) 默认情况下,如果您通过http.formLogin(). 这里每个请求都由JSESSIONID关联的会话(cookie)标识。如果请求不包含有效会话,那么它将被重定向到身份验证入口点(例如:登录页面)。
api-url-pattern = "/api/**"
webApp-url-pattern = "/**"
Run Code Online (Sandbox Code Playgroud)
这个怎么运作
URL/api/**将通过JwtAuthTokenFilter它读取令牌的位置,如果它具有有效令牌,则设置身份验证对象并继续链。如果它没有有效的请求,那么链就会被破坏,并且响应将以 401(未授权)状态发送。
URL/api/**将被处理UsernamePasswordAuthenticationFilter[这是由.formLogin()配置配置的spring 安全性中的默认值] 它将检查有效的会话,如果它不包含有效的会话,它将重定向到配置的 logoutSuccessUrl。
如何配置注意: 您的 Web 应用程序无法使用现有会话访问 API。您可以选择使用 Jwt 令牌从 Web 应用程序访问 API。
要实现两种不同的身份验证处理过滤器,您应该以不同的顺序配置多个 http 安全配置,
可以通过在您的安全配置类中声明静态类来配置多个 http 安全配置,如下所示。
(尽管 OP 询问了概念明智地提出它的代码明智。它可以帮助您参考)
api-url-pattern = "/api/**"
webApp-url-pattern = "/**"
Run Code Online (Sandbox Code Playgroud)
Jwt 身份验证令牌过滤器
@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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
调度程序 Servlet 配置
@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));
}
}
Run Code Online (Sandbox Code Playgroud)
上面的解释是一种类型的实现,我已经在我的另一个答案中解释了其他类型的实现(其中 Rest API 可以通过身份验证令牌和会话访问),您可以参考这里
| 归档时间: |
|
| 查看次数: |
2751 次 |
| 最近记录: |