电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解

瀏覽:4日期:2023-03-25 14:16:11
1 簡(jiǎn)介

Spring Security作為成熟且強(qiáng)大的安全框架,得到許多大廠的青睞。而作為前后端分離的SSO方案,JWT也在許多項(xiàng)目中應(yīng)用。本文將介紹如何通過(guò)Spring Security實(shí)現(xiàn)JWT認(rèn)證。

用戶(hù)與服務(wù)器交互大概如下:

Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解

客戶(hù)端獲取JWT,一般通過(guò)POST方法把用戶(hù)名/密碼傳給server; 服務(wù)端接收到客戶(hù)端的請(qǐng)求后,會(huì)檢驗(yàn)用戶(hù)名/密碼是否正確,如果正確則生成JWT并返回;不正確則返回錯(cuò)誤; 客戶(hù)端拿到JWT后,在有效期內(nèi)都可以通過(guò)JWT來(lái)訪問(wèn)資源了,一般把JWT放在請(qǐng)求頭;一次獲取,多次使用; 服務(wù)端校驗(yàn)JWT是否合法,合法則允許客戶(hù)端正常訪問(wèn),不合法則返回401。 2 項(xiàng)目整合

我們把要整合的Spring Security和JWT加入到項(xiàng)目的依賴(lài)中去:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><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>2.1 JWT整合2.1.1 JWT工具類(lèi)

JWT工具類(lèi)起碼要具有以下功能:

根據(jù)用戶(hù)信息生成JWT; 校驗(yàn)JWT是否合法,如是否被篡改、是否過(guò)期等; 從JWT中解析用戶(hù)信息,如用戶(hù)名、權(quán)限等;

具體代碼如下:

@Componentpublic class JwtTokenProvider { @Autowired JwtProperties jwtProperties; @Autowired private CustomUserDetailsService userDetailsService; private String secretKey; @PostConstruct protected void init() { secretKey = Base64.getEncoder().encodeToString(jwtProperties.getSecretKey().getBytes()); } public String createToken(String username, List<String> roles) { Claims claims = Jwts.claims().setSubject(username); claims.put('roles', roles); Date now = new Date(); Date validity = new Date(now.getTime() + jwtProperties.getValidityInMs()); return Jwts.builder()// .setClaims(claims)// .setIssuedAt(now)// .setExpiration(validity)// .signWith(SignatureAlgorithm.HS256, secretKey)// .compact(); } public Authentication getAuthentication(String token) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token)); return new UsernamePasswordAuthenticationToken(userDetails, '', userDetails.getAuthorities()); } public String getUsername(String token) { return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); } public String resolveToken(HttpServletRequest req) { String bearerToken = req.getHeader('Authorization'); if (bearerToken != null && bearerToken.startsWith('Bearer ')) { return bearerToken.substring(7); } return null; } public boolean validateToken(String token) { try { Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); if (claims.getBody().getExpiration().before(new Date())) { return false; } return true; } catch (JwtException | IllegalArgumentException e) { throw new InvalidJwtAuthenticationException('Expired or invalid JWT token'); } }}

工具類(lèi)還實(shí)現(xiàn)了另一個(gè)功能:從HTTP請(qǐng)求頭中獲取JWT。

2.1.2 Token處理的Filter

Filter是Security處理的關(guān)鍵,基本上都是通過(guò)Filter來(lái)攔截請(qǐng)求的。首先從請(qǐng)求頭取出JWT,然后校驗(yàn)JWT是否合法,如果合法則取出Authentication保存在SecurityContextHolder里。如果不合法,則做異常處理。

public class JwtTokenAuthenticationFilter extends GenericFilterBean { private JwtTokenProvider jwtTokenProvider; public JwtTokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { this.jwtTokenProvider = jwtTokenProvider; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { String token = jwtTokenProvider.resolveToken(request); if (token != null && jwtTokenProvider.validateToken(token)) { Authentication auth = jwtTokenProvider.getAuthentication(token); if (auth != null) { SecurityContextHolder.getContext().setAuthentication(auth); } } } catch (InvalidJwtAuthenticationException e) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write('Invalid token'); response.getWriter().flush(); return; } filterChain.doFilter(req, res); }}

對(duì)于異常處理,使用@ControllerAdvice是不行的,應(yīng)該這個(gè)是Filter,在這里拋的異常還沒(méi)有到DispatcherServlet,無(wú)法處理。所以Filter要自己做異常處理:

catch (InvalidJwtAuthenticationException e) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter().write('Invalid token'); response.getWriter().flush(); return;}

最后的return不能省略,因?yàn)橐呀?jīng)要把輸出的內(nèi)容給Response了,沒(méi)有必要再往后傳遞,否則報(bào)錯(cuò)

java.lang.IllegalStateException: getWriter() has already been called2.1.3 JWT屬性

JWT需要配置一個(gè)密鑰來(lái)加密,同時(shí)還要配置JWT令牌的有效期。

@Configuration@ConfigurationProperties(prefix = 'pkslow.jwt')public class JwtProperties { private String secretKey = 'pkslow.key'; private long validityInMs = 3600_000;//getter and setter}2.2 Spring Security整合

Spring Security的整個(gè)框架還是比較復(fù)雜的,簡(jiǎn)化后大概如下圖所示:

Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解

它是通過(guò)一連串的Filter來(lái)進(jìn)行安全管理。細(xì)節(jié)這里先不展開(kāi)講。

2.2.1 WebSecurityConfigurerAdapter配置

這個(gè)配置也可以理解為是FilterChain的配置,可以不用理解,代碼很好懂它做了什么:

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired JwtTokenProvider jwtTokenProvider; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().disable() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers('/auth/login').permitAll() .antMatchers(HttpMethod.GET, '/admin').hasRole('ADMIN') .antMatchers(HttpMethod.GET, '/user').hasRole('USER') .anyRequest().authenticated() .and() .apply(new JwtSecurityConfigurer(jwtTokenProvider)); }}

這里通過(guò)HttpSecurity配置了哪些請(qǐng)求需要什么權(quán)限才可以訪問(wèn)。

/auth/login用于登陸獲取JWT,所以都能訪問(wèn); /admin只有ADMIN用戶(hù)才可以訪問(wèn); /user只有USER用戶(hù)才可以訪問(wèn)。

而之前實(shí)現(xiàn)的Filter則在下面配置使用:

public class JwtSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { private JwtTokenProvider jwtTokenProvider; public JwtSecurityConfigurer(JwtTokenProvider jwtTokenProvider) { this.jwtTokenProvider = jwtTokenProvider; } @Override public void configure(HttpSecurity http) throws Exception { JwtTokenAuthenticationFilter customFilter = new JwtTokenAuthenticationFilter(jwtTokenProvider); http.exceptionHandling() .authenticationEntryPoint(new JwtAuthenticationEntryPoint()) .and() .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); }}2.2.2 用戶(hù)從哪來(lái)

通常在Spring Security的世界里,都是通過(guò)實(shí)現(xiàn)UserDetailsService來(lái)獲取UserDetails的。

@Componentpublic class CustomUserDetailsService implements UserDetailsService { private UserRepository users; public CustomUserDetailsService(UserRepository users) { this.users = users; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return this.users.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException('Username: ' + username + ' not found')); }}

對(duì)于UserRepository,可以從數(shù)據(jù)庫(kù)中讀取,或者其它用戶(hù)管理中心。為了方便,我使用Map放了兩個(gè)用戶(hù):

@Repositorypublic class UserRepository { private static final Map<String, User> allUsers = new HashMap<>(); @Autowired private PasswordEncoder passwordEncoder; @PostConstruct protected void init() { allUsers.put('pkslow', new User('pkslow', passwordEncoder.encode('123456'), Collections.singletonList('ROLE_ADMIN'))); allUsers.put('user', new User('user', passwordEncoder.encode('123456'), Collections.singletonList('ROLE_USER'))); } public Optional<User> findByUsername(String username) { return Optional.ofNullable(allUsers.get(username)); }}3 測(cè)試

完成代碼編寫(xiě)后,我們來(lái)測(cè)試一下:

(1)無(wú)JWT訪問(wèn),失敗

curl http://localhost:8080/admin{'timestamp':'2021-02-06T05:45:06.385+0000','status':403,'error':'Forbidden','message':'Access Denied','path':'/admin'}$ curl http://localhost:8080/user{'timestamp':'2021-02-06T05:45:16.438+0000','status':403,'error':'Forbidden','message':'Access Denied','path':'/user'}

(2)admin獲取JWT,密碼錯(cuò)誤則失敗,密碼正確則成功

$ curl http://localhost:8080/auth/login -X POST -d ’{'username':'pkslow','password':'xxxxxx'}’ -H ’Content-Type: application/json’{'timestamp':'2021-02-06T05:47:16.254+0000','status':403,'error':'Forbidden','message':'Access Denied','path':'/auth/login'}$ curl http://localhost:8080/auth/login -X POST -d ’{'username':'pkslow','password':'123456'}’ -H ’Content-Type: application/json’eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo

(3)admin帶JWT訪問(wèn)/admin,成功;訪問(wèn)/user失敗

$ curl http://localhost:8080/admin -H ’Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo’you are admin$ curl http://localhost:8080/user -H ’Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDYxNCwiZXhwIjoxNjEyNTkxMjE0fQ.d4Gi50aaOsHHqpM0d8Mh1960otnZf7rlE3x6xSfakVo’{'timestamp':'2021-02-06T05:51:23.099+0000','status':403,'error':'Forbidden','message':'Forbidden','path':'/user'}

(4)使用過(guò)期的JWT訪問(wèn),失敗

$ curl http://localhost:8080/admin -H ’Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJwa3Nsb3ciLCJyb2xlcyI6WyJST0xFX0FETUlOIl0sImlhdCI6MTYxMjU5MDQ0OSwiZXhwIjoxNjEyNTkwNTA5fQ.CSaubE4iJcYATbLmbb59aNFU1jNCwDFHUV3zIakPU64’Invalid token4 總結(jié)

代碼請(qǐng)查看:https://github.com/LarryDpk/pkslow-samples

以上就是Springboot集成Spring Security實(shí)現(xiàn)JWT認(rèn)證的步驟詳解的詳細(xì)內(nèi)容,更多關(guān)于Springboot集成Spring Security的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 能量回馈_制动单元_电梯节能_能耗制动_深圳市合兴加能科技有限公司 | 真空包装机-诸城市坤泰食品机械有限公司| Copeland/谷轮压缩机,谷轮半封闭压缩机,谷轮涡旋压缩机,型号规格,技术参数,尺寸图片,价格经销商 CTP磁天平|小电容测量仪|阴阳极极化_双液系沸点测定仪|dsj电渗实验装置-南京桑力电子设备厂 | 金环宇|金环宇电线|金环宇电缆|金环宇电线电缆|深圳市金环宇电线电缆有限公司|金环宇电缆集团 | 400电话_400电话申请_888元包年_400电话办理服务中心_400VIP网 | 订做不锈钢_不锈钢定做加工厂_不锈钢非标定制-重庆侨峰金属加工厂 | 选宝石船-陆地水上开采「精选」色选机械设备-青州冠诚重工机械有限公司 | 国际学校_国际学校哪个好_国际课程学校-国际学校择校网 | 短信通106短信接口验证码接口群发平台_国际短信接口验证码接口群发平台-速度网络有限公司 | 大倾角皮带机-皮带输送机-螺旋输送机-矿用皮带输送机价格厂家-河南坤威机械 | 环讯传媒,永康网络公司,永康网站建设,永康小程序开发制作,永康网站制作,武义网页设计,金华地区网站SEO优化推广 - 永康市环讯电子商务有限公司 | 太原装修公司_山西整装家装设计_太原室内装潢软装_肖邦家居 | 品牌广告服务平台,好排名,好流量,好生意。 | 数显恒温油浴-电砂浴-高温油浴振荡器-常州迈科诺仪器有限公司 | 防爆鼓风机-全风-宏丰鼓风机-上海梁瑾机电设备有限公司 | 琉璃瓦-琉璃瓦厂家-安徽盛阳新型建材科技有限公司 | 广州二手电缆线回收,旧电缆回收,广州铜线回收-广东益福电缆线回收公司 | 螺杆式冷水机-低温冷水机厂家-冷冻机-风冷式-水冷式冷水机-上海祝松机械有限公司 | 搅拌磨|搅拌球磨机|循环磨|循环球磨机-无锡市少宏粉体科技有限公司 | 山东钢衬塑罐_管道_反应釜厂家-淄博富邦滚塑防腐设备科技有限公司 | 青岛美佳乐清洁工程有限公司|青岛油烟管道清洗|酒店|企事业单位|学校工厂厨房|青岛油烟管道清洗 插针变压器-家用电器变压器-工业空调变压器-CD型电抗器-余姚市中驰电器有限公司 | 河南卓美创业科技有限公司-河南卓美防雷公司-防雷接地-防雷工程-重庆避雷针-避雷器-防雷检测-避雷带-避雷针-避雷塔、机房防雷、古建筑防雷等-山西防雷公司 | 等离子表面处理机-等离子表面活化机-真空等离子清洗机-深圳市东信高科自动化设备有限公司 | 铝镁锰板_铝镁锰合金板_铝镁锰板厂家_铝镁锰金属屋面板_安徽建科 | 余姚生活网_余姚论坛_余姚市综合门户网站 | 全自动五线打端沾锡机,全自动裁线剥皮双头沾锡机,全自动尼龙扎带机-东莞市海文能机械设备有限公司 | 合肥钣金加工-安徽激光切割加工-机箱机柜加工厂家-合肥通快 | 2025世界机器人大会_IC China_半导体展_集成电路博览会_智能制造展览网 | 阴离子_阳离子聚丙烯酰胺厂家_聚合氯化铝价格_水处理絮凝剂_巩义市江源净水材料有限公司 | 即用型透析袋,透析袋夹子,药敏纸片,L型涂布棒-上海桥星贸易有限公司 | 深圳成考网-深圳成人高考报名网 深圳工程师职称评定条件及流程_深圳职称评审_职称评审-职称网 | 精密钢管,冷拔精密无缝钢管,精密钢管厂,精密钢管制造厂家,精密钢管生产厂家,山东精密钢管厂家 | 传爱自考网_传爱自学考试网 | 东莞ERP软件_广州云ERP_中山ERP_台湾工厂erp系统-广东顺景软件科技有限公司 | 木材烘干机,木炭烘干机,纸管/佛香烘干设备-河南蓝天机械制造有限公司 | 北京银联移动POS机办理_收银POS机_智能pos机_刷卡机_收银系统_个人POS机-谷骐科技【官网】 | 石家庄网站建设|石家庄网站制作|石家庄小程序开发|石家庄微信开发|网站建设公司|网站制作公司|微信小程序开发|手机APP开发|软件开发 | 骨密度检测仪_骨密度分析仪_骨密度仪_动脉硬化检测仪专业生产厂家【品源医疗】 | Safety light curtain|Belt Sway Switches|Pull Rope Switch|ultrasonic flaw detector-Shandong Zhuoxin Machinery Co., Ltd | 在线浊度仪_悬浮物污泥浓度计_超声波泥位计_污泥界面仪_泥水界面仪-无锡蓝拓仪表科技有限公司 | TPM咨询,精益生产管理,5S,6S现场管理培训_华谋咨询公司 |