spring security 使用JWT進(jìn)行權(quán)限認(rèn)證

spring security 使用JWT進(jìn)行權(quán)限認(rèn)證

pom

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 ?
 <dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
 </dependency>
 ?
 <dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.0</version>
  <scope>compile</scope>
 </dependency>
 ?
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.68</version>
 </dependency></pre>

配置類

 /**
  * @author spp
  * @date 2020-06-11 14:17
  **/
 @EnableWebSecurity
 public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  public void configure(WebSecurity web) throws Exception {
  //忽略請求,不經(jīng)過security過濾器鏈
  web.ignoring().mvcMatchers(HttpMethod.GET,"/**");
  }
 ?
  /**
  * 從容器中取出 AuthenticationManagerBuilder,執(zhí)行方法里面的邏輯之后,放回容器
  * @param authenticationManagerBuilder x
  * @throws Exception
  */
  @Autowired
  public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
  authenticationManagerBuilder.userDetailsService(userDetails).passwordEncoder(new BCryptPasswordEncoder());
  }
 ?
  @Override
  public void configure(HttpSecurity http) throws Exception {
  //解決跨域問題。cors 預(yù)檢請求放行,讓Spring security 放行所有preflight request(cors 預(yù)檢請求)
  http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
  //讓Security永遠(yuǎn)不會創(chuàng)建HttpSession宇葱,它不會使用HttpSession來獲取SecurityContext
  http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  .and().headers().cacheControl();
 ?
  http.authorizeRequests()
  .antMatchers("/admin/**")
  .hasAuthority("root")
  .antMatchers("/").permitAll();
 ?
  //token權(quán)限解析認(rèn)證
  http.addFilterBefore(authJwtFilter,UsernamePasswordAuthenticationFilter.class);
 ?
  //登陸
  http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  //處理異常情況:認(rèn)證失敗和權(quán)限不足
  http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
  response.setContentType("application/json;charset=utf8");
  Object error = request.getAttribute("error");
  if (ObjectUtils.isEmpty(error)){
  response.getWriter().println("權(quán)限不足");
  return;
  }
  response.getWriter().println(error);
  }).accessDeniedHandler((request, response, accessDeniedException) -> {
  response.setContentType("application/json;charset=utf8");
  response.getWriter().println("你的權(quán)限不足以訪問該資源");
  });
  }
 ?
  /**
  * 登陸攔截器
  * @return
  * @throws Exception
  */
  @Bean
  public UsernamePassAuthFilter myUsernamePasswordAuthenticationFilter() throws Exception {
  UsernamePassAuthFilter filter = new UsernamePassAuthFilter();
  //成功后處理
  filter.setAuthenticationSuccessHandler(authSuccessHandler);
  //失敗后處理
  filter.setAuthenticationFailureHandler(authFailHandler);
  filter.setAuthenticationManager(authenticationManagerBean());
  return filter;
  }
 ?
 }

最重要的http.addFilterBefore(authJwtFilter,UsernamePasswordAuthenticationFilter.class);

JWT過濾器

 /**
  * @author spp
  * @date 2020-06-11 16:21
  * jwt token 認(rèn)證解析攔截器
  **/
 @Component
 public class AuthJwtFilter extends OncePerRequestFilter {
  public final  String HEADER = "Authorization";
 ?
  /**
  * @param request rq
  * @param response rs
  * @param filterChain 鏈
  */
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  String token = request.getHeader(HEADER);
  if (!ObjectUtils.isEmpty(token)){
  logger.info("token-->"+token);
  //是否過期
  boolean check = false;
  try {
  check = JwtTokenUtil.isTokenExpired(token);
  } catch (Exception e) {
  request.setAttribute("error",e.getMessage());
  }
  if (!check){
  String username = JwtTokenUtil.getUsernameFromToken(token);
  if (username != null){
  //通過用戶信息得到UserDetails
  List<SimpleGrantedAuthority> list = new ArrayList<>();
  list.add(new SimpleGrantedAuthority("root"));
  AuthUser authUser = new AuthUser(username,null,list);
  //將用戶信息存入 authentication,方便后續(xù)校驗
  UsernamePasswordAuthenticationToken authentication =
  new UsernamePasswordAuthenticationToken(
  authUser.getUsername(),
  null,
  authUser.getAuthorities()
  );
  authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
  // 將 authentication 存入 ThreadLocal,方便后續(xù)獲取用戶信息
  SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  }
 ?
  }
  filterChain.doFilter(request, response);
  }
 }

JWT工具類

 /**
  * JWT生成令牌唯卖、驗證令牌、獲取令牌
  */
 @Component
 @NoArgsConstructor
 public class JwtTokenUtil {
  /**
  * 私鑰
  */
 ?
  private static final String SECRET_KEY = "auth_sp";
 ?
  /**
  * 過期時間 毫秒,設(shè)置默認(rèn)1周的時間過期
  */
  private static final long EXPIRATION_TIME = 3600000L * 2;
 ?
  /**
  * 生成令牌
  * @param userDetails 用戶
  * @return 令牌
  */
  public static String generateToken(UserDetails userDetails) {
  Map<String, Object> claims = new HashMap<>(2);
  claims.put(Claims.SUBJECT, userDetails.getUsername());
  claims.put(Claims.ISSUED_AT, new Date());
  return generateToken(claims);
  }
 ?
  /**
  * 從令牌中獲取用戶名
  * @param token 令牌
  * @return 用戶名
  */
  public static String getUsernameFromToken(String token) {
  String username = null;
  try {
  Claims claims = getClaimsFromToken(token);
  username = claims.getSubject();
  } catch (Exception e) {
  System.out.println("e = " + e.getMessage());
  }
  return username;
  }
 ?
  /**
  * 判斷令牌是否過期
  *
  * @param token 令牌
  * @return 是否過期
  */
  public static Boolean isTokenExpired(String token) throws  Exception{
  try {
  Claims claims = getClaimsFromToken(token);
  Date expiration = claims.getExpiration();
  return expiration.before(new Date());
  } catch (Exception e) {
  throw new Exception("簽名過期");
  }
  }
 ?
  /**
  * 刷新令牌
  *
  * @param token 原令牌
  * @return 新令牌
  */
  public static String refreshToken(String token) {
  String refreshedToken;
  try {
  Claims claims = getClaimsFromToken(token);
  claims.put(Claims.ISSUED_AT, new Date());
  refreshedToken = generateToken(claims);
  } catch (Exception e) {
  refreshedToken = null;
  }
  return refreshedToken;
  }
 ?
  /**
  * 驗證令牌
  *
  * @param token       令牌
  * @param userDetails 用戶
  * @return 是否有效
  */
  public static Boolean validateToken(String token, UserDetails userDetails) throws Exception {
  AuthUser user = (AuthUser) userDetails;
  String username = getUsernameFromToken(token);
  return (username.equals(user.getUsername()) && !isTokenExpired(token));
  }
 ?
  /**
  * 從數(shù)據(jù)聲明生成令牌
  *
  * @param claims 數(shù)據(jù)聲明
  * @return 令牌
  */
  private static String generateToken(Map<String, Object> claims) {
  Date expirationDate = new Date(System.currentTimeMillis()+ EXPIRATION_TIME);
  HashMap<String, Object> map = new HashMap<>(1);map.put("typ",Header.JWT_TYPE);
  return Jwts.builder().setHeader(map).setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
  }
 ?
  /**
  * 從令牌中獲取數(shù)據(jù)聲明
  *
  * @param token 令牌
  * @return 數(shù)據(jù)聲明
  */
  private static Claims getClaimsFromToken(String token) throws Exception {
  Claims claims = null;
  try {
  claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
  } catch (Exception e) {
  new Throwable(e);
  }
  return claims;
  }
 }

測試

不攜帶token

攜帶正確的token

故意將token破壞

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躬柬,一起剝皮案震驚了整個濱河市拜轨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌允青,老刑警劉巖橄碾,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡法牲,警方通過查閱死者的電腦和手機(jī)史汗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拒垃,“玉大人蚜锨,你說我怎么就攤上這事衔峰⌒奕唬” “怎么了硼啤?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谤牡。 經(jīng)常有香客問我副硅,道長,這世上最難降的妖魔是什么翅萤? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任恐疲,我火速辦了婚禮,結(jié)果婚禮上套么,老公的妹妹穿的比我還像新娘培己。我一直安慰自己,他們只是感情好胚泌,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布省咨。 她就那樣靜靜地躺著,像睡著了一般玷室。 火紅的嫁衣襯著肌膚如雪零蓉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天穷缤,我揣著相機(jī)與錄音敌蜂,去河邊找鬼。 笑死津肛,一個胖子當(dāng)著我的面吹牛章喉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播身坐,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秸脱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了部蛇?” 一聲冷哼從身側(cè)響起摊唇,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涯鲁,沒想到半個月后巷查,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘹害,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年吮便,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幢踏。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡髓需,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出房蝉,到底是詐尸還是另有隱情僚匆,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布搭幻,位于F島的核電站咧擂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏檀蹋。R本人自食惡果不足惜松申,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俯逾。 院中可真熱鬧贸桶,春花似錦、人聲如沸桌肴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坠七。三九已至水醋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間彪置,已是汗流浹背拄踪。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留悉稠,地道東北人宫蛆。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像的猛,于是被迫代替她去往敵國和親耀盗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355