問題描述
我有一種情況,我正在使用 Spring MVC(jsp、控制器、服務、dao)和基于會話的身份驗證.但是現在我將幾個 url 用作 RESTful Web 服務來進行集成.
I have a situation, I am using Spring MVC (jsp, controllers, service, dao) and session based authentication. But now few urls I am using as a RESTful Web service for integration purpose.
僅對于那些請求,我需要使用基于令牌(例如 JWT)的身份驗證.
For those requests only, I need to use token (for eg JWT) based authentication.
那么,有沒有可能我可以在同一個項目中使用這兩種類型的身份驗證.
So, is there any possibility that I can use both type of authentication within same project.
推薦答案
有沒有可能我可以在同一個項目中使用這兩種類型的身份驗證.
is there any possibility that I can use both type of authentication within same project.
是的,你可以.通過具有兩個身份驗證處理過濾器.
Yes you can. By having two authentication processing filters.
Filter - 1:用于 Rest API (JwtAuthTokenFilter),它應該是無狀態的,并由每次在請求中發送的授權令牌標識.
Filter - 2:你需要另一個過濾器(UsernamePasswordAuthenticationFilter) 默認情況下,如果你通過 http.formLogin()
配置它,spring-security 會提供這個.這里每個請求都由關聯的會話(JSESSIONID
cookie)標識.如果請求不包含有效會話,那么它將被重定向到身份驗證入口點(例如:登錄頁面).
Filter - 1: for Rest API (JwtAuthTokenFilter) which should be stateless and identified by Authorization token sent in request each time.
Filter - 2: You need another filter (UsernamePasswordAuthenticationFilter) By default spring-security provides this if you configure it by http.formLogin()
. Here each request is identified by the session(JSESSIONID
cookie) associated. If request does not contain valid session then it will be redirected to authentication-entry-point (say: login-page).
api-url-pattern = "/api/**"
webApp-url-pattern = "/**"
工作原理
帶有
/api/**
的 URL 將通過JwtAuthTokenFilter
傳遞,它將讀取令牌,如果它具有有效令牌,則設置身份驗證對象和鏈繼續.如果它沒有有效的請求,那么鏈會被破壞并且響應將被發送 401(未授權)狀態.How it works
URL's with
/api/**
will be passed throughJwtAuthTokenFilter
where it will read the token and if it has valid token, sets authentication object and chain continues. If it does not have the valid request then chain gets broken and response will be sent with 401(Unauthorized) status./api/**
以外的 URL 將由UsernamePasswordAuthenticationFilter
處理 [這是由.formLogin()
configuration] 它將檢查有效會話,如果它不包含有效會話,它將重定向到 logoutSuccessUrl 配置.URL's other than
/api/**
will be handled byUsernamePasswordAuthenticationFilter
[which is default in spring security configured by.formLogin()
configuration] It will check for valid session, if it does not contain the valid session it will redirects to logoutSuccessUrl configured.注意:您的 Web 應用程序無法使用現有會話訪問 API.您有什么選擇是使用 Jwt 令牌從 Web 應用程序訪問 API.
Note: Your Webapp can not access APIs by using existing session. What option you have is to use Jwt token to access API from Web application.
如何配置
實現兩種不同的身份驗證處理過濾器,您應該以不同的順序配置多個 http 安全配置
可以通過在安全配置類中聲明靜態類來配置多個 http 安全配置,如下所示.
(盡管 OP 要求概念明智地呈現代碼明智.它可能會幫助您參考)@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)); } }
Dispatcher Servlet 配置
@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/"); } }
以上解釋是一種實現,我已經在 我的另一個答案,你可以在這里參考
Above explanation is one type of implementation, i have explained other type of implementation(where Rest APIs can be accessed by auth token as well as session) in my another answer which you can refer here
這篇關于混合身份驗證 - 基于 Spring MVC 會話 + 基于 JWT 令牌的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!
【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!