<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
security進行使用者驗證和授權;jwt負責頒發令牌與校驗,判斷使用者登入狀態
SpringSecurity 採用的是責任鏈的設計模式,它有一條很長的過濾器鏈。
流程說明:使用者端發起一個請求,進入 Security 過濾器鏈。
當到 LogoutFilter 的時候判斷是否是登出路徑,如果是登出路徑則到 logoutHandler ,如果登出成功則到 logoutSuccessHandler 登出成功處理,如果登出失敗則由 ExceptionTranslationFilter ;如果不是登出路徑則直接進入下一個過濾器。
當到 UsernamePasswordAuthenticationFilter 的時候判斷是否為登入路徑,如果是,則進入該過濾器進行登入操作,如果登入失敗則到 AuthenticationFailureHandler 登入失敗處理器處理,如果登入成功則到 AuthenticationSuccessHandler 登入成功處理器處理,如果不是登入請求則不進入該過濾器。
當到 FilterSecurityInterceptor 的時候會拿到 uri ,根據 uri 去找對應的鑑權管理器,鑑權管理器做鑑權工作,鑑權成功則到 Controller 層否則到 AccessDeniedHandler 鑑權失敗處理器處理。
首先前端一樣是把登入資訊傳送給後端,後端查詢資料庫校驗使用者的賬號和密碼是否正確,正確的話則使用jwt生成token,並且返回給前端。以後前端每次請求時,都需要攜帶token,後端獲取token後,使用jwt進行驗證使用者的token是否無效或過期,驗證成功後才去做相應的邏輯。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
/** * Security 設定 */ @Configuration @EnableWebSecurity @RequiredArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired LoginFailureHandler loginFailureHandler; @Autowired LoginSuccessHandler loginSuccessHandler; @Autowired JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired JwtAccessDeniedHandler jwtAccessDeniedHandler; @Autowired UserDetailServiceImpl userDetailService; @Autowired JWTLogoutSuccessHandler jwtLogoutSuccessHandler; @Autowired CaptchaFilter captchaFilter; @Value("${security.enable}") private Boolean securityIs = Boolean.TRUE; @Value("${security.permit}") private String permit; @Bean public HttpFirewall allowUrlEncodedSlashHttpFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); //此處可新增別的規則,目前只設定 允許雙 // firewall.setAllowUrlEncodedDoubleSlash(true); return firewall; } @Bean JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager(), jwtAuthenticationEntryPoint); return jwtAuthenticationFilter; } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.cors().and().csrf().disable() // 登入設定 .formLogin() .successHandler(loginSuccessHandler) .failureHandler(loginFailureHandler) .and() .logout() .logoutSuccessHandler(jwtLogoutSuccessHandler) // 禁用session .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 設定攔截規則 .and() .authorizeRequests() .antMatchers(permit.split(",")).permitAll(); if (!securityIs) { http.authorizeRequests().antMatchers("/**").permitAll(); } registry.anyRequest().authenticated() // 例外處理器 .and() .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(jwtAccessDeniedHandler) // 設定自定義的過濾器 .and() .addFilter(jwtAuthenticationFilter()) // 驗證碼過濾器放在UsernamePassword過濾器之前 .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailService); } }
package cn.piesat.gf.filter; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import cn.piesat.gf.dao.user.SysUserDao; import cn.piesat.gf.model.entity.user.SysUser; import cn.piesat.gf.exception.ExpiredAuthenticationException; import cn.piesat.gf.exception.MyAuthenticationException; import cn.piesat.gf.service.user.impl.UserDetailServiceImpl; import cn.piesat.gf.utils.Constants; import cn.piesat.gf.utils.JwtUtils; import cn.piesat.gf.utils.Result; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; @Slf4j public class JwtAuthenticationFilter extends BasicAuthenticationFilter { private AuthenticationEntryPoint authenticationEntryPoint; private AuthenticationManager authenticationManager; @Autowired JwtUtils jwtUtils; @Autowired UserDetailServiceImpl userDetailService; @Autowired SysUserDao sysUserRepository; @Autowired RedisTemplate redisTemplate; @Value("${security.single}") private Boolean singleLogin = false; public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) { super(authenticationManager, authenticationEntryPoint); Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null"); this.authenticationManager = authenticationManager; this.authenticationEntryPoint = authenticationEntryPoint; } public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String jwt = request.getHeader(jwtUtils.getHeader()); // 這裡如果沒有jwt,繼續往後走,因為後面還有鑑權管理器等去判斷是否擁有身份憑證,所以是可以放行的 // 沒有jwt相當於匿名存取,若有一些介面是需要許可權的,則不能存取這些介面 if (StrUtil.isBlankOrUndefined(jwt)) { chain.doFilter(request, response); return; } try { Claims claim = jwtUtils.getClaimsByToken(jwt); if (claim == null) { throw new MyAuthenticationException("token 異常"); } if (jwtUtils.isTokenExpired(claim)) { throw new MyAuthenticationException("token 已過期"); } String username = claim.getSubject(); Object o1 = redisTemplate.opsForValue().get(Constants.TOKEN_KEY + username); String o = null; if(!ObjectUtils.isEmpty(o1)){ o = o1.toString(); } if (!StringUtils.hasText(o)) { throw new ExpiredAuthenticationException("您的登入資訊已過期,請重新登入!"); } if (singleLogin && StringUtils.hasText(o) && !jwt.equals(o)) { throw new MyAuthenticationException("您的賬號已別處登入,您已下線,如有異常請及時修改密碼!"); } // 獲取使用者的許可權等資訊 SysUser sysUser = sysUserRepository.findByUserName(username); // 構建UsernamePasswordAuthenticationToken,這裡密碼為null,是因為提供了正確的JWT,實現自動登入 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, userDetailService.getUserAuthority(sysUser.getUserId())); SecurityContextHolder.getContext().setAuthentication(token); chain.doFilter(request, response); } catch (AuthenticationException e) { log.error(ExceptionUtil.stacktraceToString(e)); authenticationEntryPoint.commence(request, response, e); return; } catch (Exception e){ log.error(ExceptionUtil.stacktraceToString(e)); response.getOutputStream().write(JSONUtil.toJsonStr(Result.fail(e.getMessage())).getBytes(StandardCharsets.UTF_8)); response.getOutputStream().flush(); response.getOutputStream().close(); return; } } }
package cn.piesat.gf.utils; import cn.hutool.core.exceptions.ExceptionUtil; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Date; @Data @Component @ConfigurationProperties(prefix = "jwt.config") @Slf4j public class JwtUtils { private long expire; private String secret; private String header; // 生成JWT public String generateToken(String username) { Date nowDate = new Date(); Date expireDate = new Date(nowDate.getTime() + 1000 * expire); return Jwts.builder() .setHeaderParam("typ", "JWT") .setSubject(username) .setIssuedAt(nowDate) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } // 解析JWT public Claims getClaimsByToken(String jwt) { try { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(jwt) .getBody(); } catch (Exception e) { log.error(ExceptionUtil.stacktraceToString(e)); return null; } } // 判斷JWT是否過期 public boolean isTokenExpired(Claims claims) { return claims.getExpiration().before(new Date()); } }
到此這篇關於SpringBoot SpringSecurity JWT實現系統安全策略詳解的文章就介紹到這了,更多相關SpringBoot SpringSecurity JWT內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45