<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                > 原文閱讀:https://blog.csdn.net/jpgzhu/article/details/105200598 > - 本文內容已應用于dmp框架,并對com.keyou.dmp.util.JwtTokenUtils#getAuthentication進行了優化,增加了權限信息的存放,最新代碼請查看dmp; > - 本框架是在SpringSecurity的基礎上引入了jwt,并沒有引入oauth2。當然SpringSecurity在引入OAuth2的前提下,可以使用jwt作為token存儲方案,如ocp就是這樣使用的。 #### 前言 > 微服務架構,前后端分離目前已成為互聯網項目開發的業界標準,其核心思想就是前端(APP、小程序、H5頁面等)通過調用后端的API接口,提交及返回JSON數據進行交互。 > 在前后端分離項目中,首先要解決的就是登錄及授權的問題。微服務架構下,傳統的session認證限制了應用的擴展能力,無狀態的JWT認證方法應運而生,該認證機制特別適用于分布式站點的單點登錄(SSO)場景 #### 目錄 > 該文會通過創建SpringBoot項目整合SpringSecurity,實現完整的JWT認證機制,主要步驟如下: > > 1. 創建SpringBoot工程 > 2. 導入SpringSecurity與JWT的相關依賴 > 3. 定義SpringSecurity需要的基礎處理類 > 4. 構建JWT token工具類 > 5. 實現token驗證的過濾器 > 6. SpringSecurity的關鍵配置 > 7. 編寫Controller進行測試 #### 1、創建SpringBoot工程 ![SpringBoot工程項目目錄](https://img-blog.csdnimg.cn/20200330155505528.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2pwZ3podQ==,size_16,color_FFFFFF,t_70) #### 2、導入SpringSecurity與JWT的相關依賴 pom文件加入以下依賴 ```java <!--Security框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ... <!-- jwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.10.6</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.10.6</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.10.6</version> </dependency> ``` #### 3.定義SpringSecurity需要的基礎處理類 application.yml配置中加入jwt配置信息: ```java #jwt jwt: header: Authorization # 令牌前綴 token-start-with: Bearer # 使用Base64對該令牌進行編碼 base64-secret: XXXXXXXXXXXXXXXX(制定您的密鑰) # 令牌過期時間 此處單位/毫秒 token-validity-in-seconds: 14400000 ``` 創建一個jwt的配置類,并注入Spring,便于程序中靈活調用 ```java @Data @Configuration @ConfigurationProperties(prefix = "jwt") public class JwtSecurityProperties { /** Request Headers : Authorization */ private String header; /** 令牌前綴,最后留個空格 Bearer */ private String tokenStartWith; /** Base64對該令牌進行編碼 */ private String base64Secret; /** 令牌過期時間 此處單位/毫秒 */ private Long tokenValidityInSeconds; /**返回令牌前綴 */ public String getTokenStartWith() { return tokenStartWith + " "; } } ``` 定義無權限訪問類 ```java @Component public class JwtAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage()); } } ``` 定義認證失敗處理類 ```java @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); } } ``` #### 4. 構建JWT token工具類 工具類實現創建token與校驗token功能 ```java @Slf4j @Component public class JwtTokenUtils implements InitializingBean { private final JwtSecurityProperties jwtSecurityProperties; private static final String AUTHORITIES_KEY = "auth"; private Key key; public JwtTokenUtils(JwtSecurityProperties jwtSecurityProperties) { this.jwtSecurityProperties = jwtSecurityProperties; } @Override public void afterPropertiesSet() { byte[] keyBytes = Decoders.BASE64.decode(jwtSecurityProperties.getBase64Secret()); this.key = Keys.hmacShaKeyFor(keyBytes); } public String createToken (Map<String, Object> claims) { return Jwts.builder() .claim(AUTHORITIES_KEY, claims) .setId(UUID.randomUUID().toString()) .setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtSecurityProperties.getTokenValidityInSeconds())) .compressWith(CompressionCodecs.DEFLATE) .signWith(key,SignatureAlgorithm.HS512) .compact(); } public Date getExpirationDateFromToken(String token) { Date expiration; try { final Claims claims = getClaimsFromToken(token); expiration = claims.getExpiration(); } catch (Exception e) { expiration = null; } return expiration; } public Authentication getAuthentication(String token) { Claims claims = Jwts.parser() .setSigningKey(key) .parseClaimsJws(token) .getBody(); Collection<? extends GrantedAuthority> authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); HashMap map =(HashMap) claims.get("auth"); User principal = new User(map.get("user").toString(), map.get("password").toString(), authorities); return new UsernamePasswordAuthenticationToken(principal, token, authorities); } public boolean validateToken(String authToken) { try { Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); return true; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("Invalid JWT signature."); e.printStackTrace(); } catch (ExpiredJwtException e) { log.info("Expired JWT token."); e.printStackTrace(); } catch (UnsupportedJwtException e) { log.info("Unsupported JWT token."); e.printStackTrace(); } catch (IllegalArgumentException e) { log.info("JWT token compact of handler are invalid."); e.printStackTrace(); } return false; } private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(key) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; } } ``` #### 5.實現token驗證的過濾器 該類繼承OncePerRequestFilter,顧名思義,它能夠確保在一次請求中只通過一次filter 該類使用JwtTokenUtils工具類進行token校驗 ```java @Component @Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { private JwtTokenUtils jwtTokenUtils; public JwtAuthenticationTokenFilter(JwtTokenUtils jwtTokenUtils) { this.jwtTokenUtils = jwtTokenUtils; } @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { JwtSecurityProperties jwtSecurityProperties = SpringContextHolder.getBean(JwtSecurityProperties.class); String requestRri = httpServletRequest.getRequestURI(); //獲取request token String token = null; String bearerToken = httpServletRequest.getHeader(jwtSecurityProperties.getHeader()); if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(jwtSecurityProperties.getTokenStartWith())) { token = bearerToken.substring(jwtSecurityProperties.getTokenStartWith().length()); } if (StringUtils.hasText(token) && jwtTokenUtils.validateToken(token)) { Authentication authentication = jwtTokenUtils.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestRri); } else { log.debug("no valid JWT token found, uri: {}", requestRri); } filterChain.doFilter(httpServletRequest, httpServletResponse); } } ``` > 根據SpringBoot官方讓重復執行的filter實現一次執行過程的解決方案,參見官網地址:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-disable-registration-of-a-servlet-or-filter > 在SpringBoot啟動類中,加入以下代碼: ```java @Bean public FilterRegistrationBean registration(JwtAuthenticationTokenFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; } 123456 ``` #### 6. SpringSecurity的關鍵配置 SpringBoot推薦使用配置類來代替xml配置,該類中涉及了以上幾個bean來供security使用 - JwtAccessDeniedHandler :無權限訪問 - jwtAuthenticationEntryPoint :認證失敗處理 - jwtAuthenticationTokenFilter :token驗證的過濾器 ```java package com.zhuhuix.startup.security.config; import com.fasterxml.jackson.core.filter.TokenFilter; import com.zhuhuix.startup.security.security.JwtAccessDeniedHandler; import com.zhuhuix.startup.security.security.JwtAuthenticationEntryPoint; import com.zhuhuix.startup.security.security.JwtAuthenticationTokenFilter; import com.zhuhuix.startup.security.utils.JwtTokenUtils; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * Spring Security配置類 * * @author zhuhuix * @date 2020-03-25 */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; private final JwtTokenUtils jwtTokenUtils; public WebSecurityConfig(JwtAccessDeniedHandler jwtAccessDeniedHandler, JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint, JwtTokenUtils jwtTokenUtils) { this.jwtAccessDeniedHandler = jwtAccessDeniedHandler; this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint; this.jwtTokenUtils = jwtTokenUtils; } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // 禁用 CSRF .csrf().disable() // 授權異常 .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(jwtAccessDeniedHandler) // 防止iframe 造成跨域 .and() .headers() .frameOptions() .disable() // 不創建會話 .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 放行靜態資源 .antMatchers( HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/webSocket/**" ).permitAll() // 放行swagger .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/webjars/**").permitAll() .antMatchers("/*/api-docs").permitAll() // 放行文件訪問 .antMatchers("/file/**").permitAll() // 放行druid .antMatchers("/druid/**").permitAll() // 放行OPTIONS請求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() //允許匿名及登錄用戶訪問 .antMatchers("/api/auth/**", "/error/**").permitAll() // 所有請求都需要認證 .anyRequest().authenticated(); // 禁用緩存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity .apply(new TokenConfigurer(jwtTokenUtils)); } public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { private final JwtTokenUtils jwtTokenUtils; public TokenConfigurer(JwtTokenUtils jwtTokenUtils){ this.jwtTokenUtils = jwtTokenUtils; } @Override public void configure(HttpSecurity http) { JwtAuthenticationTokenFilter customFilter = new JwtAuthenticationTokenFilter(jwtTokenUtils); http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); } } } ``` #### 7. 編寫Controller進行測試 登錄邏輯:傳遞user與password參數,返回token ```java @Slf4j @RestController @RequestMapping("/api/auth") @Api(tags = "系統授權接口") public class AuthController { private final JwtTokenUtils jwtTokenUtils; public AuthController(JwtTokenUtils jwtTokenUtils) { this.jwtTokenUtils = jwtTokenUtils; } @ApiOperation("登錄授權") @GetMapping(value = "/login") public String login(String user,String password){ Map map = new HashMap(); map.put("user",user); map.put("password",password); return jwtTokenUtils.createToken(map); } } ``` 使用IDEA Rest Client測試如下:![Request請求](https://img-blog.csdnimg.cn/20200330190123833.png)![Response返回token](https://img-blog.csdnimg.cn/20200330190153673.png) 驗證邏輯:傳遞token,驗證成功后返回用戶信息 ![Request請求,Headers加入Authorization:(token值)](https://img-blog.csdnimg.cn/20200330193526960.png)![Response結果](https://img-blog.csdnimg.cn/20200330193639420.png) token驗證錯誤返回401: ![在這里插入圖片描述](https://img-blog.csdnimg.cn/20200330201138809.png) >其他資料: [SpringBoot整合SpringSecurity實現JWT認證](https://blog.csdn.net/jpgzhu/article/details/105200598) [Spring Security登錄認證【前后端分離】](https://blog.csdn.net/godleaf/article/details/108318403) [Springboot+SpringSecurity_jwt整合原理](https://www.processon.com/view/5eee114a07912929cb51e062?fromnew=1#map)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看