最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn)

 更新時間:2020年04月21日 16:31:36   作者:阿懶土靈  
這篇文章主要介紹了SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

廢話

目前流行的前后端分離讓Java程序員可以更加專注的做好后臺業(yè)務(wù)邏輯的功能實現(xiàn),提供如返回Json格式的數(shù)據(jù)接口就可以。SpringBoot的易用性和對其他框架的高度集成,用來快速開發(fā)一個小型應(yīng)用是最佳的選擇。

一套前后端分離的后臺項目,剛開始就要面對的就是登陸和授權(quán)的問題。這里提供一套方案供大家參考。

主要看點:

  • 登陸后獲取token,根據(jù)token來請求資源
  • 根據(jù)用戶角色來確定對資源的訪問權(quán)限
  • 統(tǒng)一異常處理
  • 返回標(biāo)準(zhǔn)的Json格式數(shù)據(jù)

正文

首先是pom文件:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <!--這是不是必須,只是我引用了里面一些類的方法-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-solr</artifactId>
    </dependency>
        <!--這是不是必須,只是我引用了里面一些類的方法-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.6.1</version>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.6.1</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-jwt</artifactId>
      <version>1.0.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

application.yml:

spring :
 datasource :
  url : jdbc:mysql://127.0.0.1:3306/les_data_center?useUnicode=true&amp;characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false
  username : root
  password : 123456
  driverClassName : com.mysql.jdbc.Driver
 jackson:
  data-format: yyyy-MM-dd HH:mm:ss
  time-zone: GMT+8
mybatis :
 config-location : classpath:/mybatis-config.xml
# JWT
jwt:
 header: Authorization
 secret: mySecret
 #token有效期一天
 expiration: 86400
 tokenHead: "Bearer "

接著是對security的配置,讓security來保護(hù)我們的API

SpringBoot推薦使用配置類來代替xml配置。那這里,我也使用配置類的方式。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  private final JwtAuthenticationEntryPoint unauthorizedHandler;

  private final AccessDeniedHandler accessDeniedHandler;

  private final UserDetailsService CustomUserDetailsService;

  private final JwtAuthenticationTokenFilter authenticationTokenFilter;

  @Autowired
  public WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler,
               @Qualifier("RestAuthenticationAccessDeniedHandler") AccessDeniedHandler accessDeniedHandler,
               @Qualifier("CustomUserDetailsService") UserDetailsService CustomUserDetailsService,
               JwtAuthenticationTokenFilter authenticationTokenFilter) {
    this.unauthorizedHandler = unauthorizedHandler;
    this.accessDeniedHandler = accessDeniedHandler;
    this.CustomUserDetailsService = CustomUserDetailsService;
    this.authenticationTokenFilter = authenticationTokenFilter;
  }

  @Autowired
  public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder
        // 設(shè)置UserDetailsService
        .userDetailsService(this.CustomUserDetailsService)
        // 使用BCrypt進(jìn)行密碼的hash
        .passwordEncoder(passwordEncoder());
  }
  // 裝載BCrypt密碼編碼器
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
        // 由于使用的是JWT,我們這里不需要csrf
        .csrf().disable()
        .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
        // 基于token,所以不需要session
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

        .authorizeRequests()

        // 對于獲取token的rest api要允許匿名訪問
        .antMatchers("/api/v1/auth", "/api/v1/signout", "/error/**", "/api/**").permitAll()
        // 除上面外的所有請求全部需要鑒權(quán)認(rèn)證
        .anyRequest().authenticated();

    // 禁用緩存
    httpSecurity.headers().cacheControl();

    // 添加JWT filter
    httpSecurity
        .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/v2/api-docs",
        "/swagger-resources/configuration/ui",
        "/swagger-resources",
        "/swagger-resources/configuration/security",
        "/swagger-ui.html"
    );
  }

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
}

該類中配置了幾個bean來供security使用。

  1. JwtAuthenticationTokenFilter:token過濾器來驗證token有效性
  2. UserDetailsService:實現(xiàn)了DetailsService接口,用來做登陸驗證
  3. JwtAuthenticationEntryPoint :認(rèn)證失敗處理類
  4. RestAuthenticationAccessDeniedHandler: 權(quán)限不足處理類

那么,接下來一個一個實現(xiàn)這些類:

/**
 * token校驗,引用的stackoverflow一個答案里的處理方式
 * Author: JoeTao
 * createAt: 2018/9/14
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

  @Value("${jwt.header}")
  private String token_header;

  @Resource
  private JWTUtils jwtUtils;

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    String auth_token = request.getHeader(this.token_header);
    final String auth_token_start = "Bearer ";
    if (StringUtils.isNotEmpty(auth_token) && auth_token.startsWith(auth_token_start)) {
      auth_token = auth_token.substring(auth_token_start.length());
    } else {
      // 不按規(guī)范,不允許通過驗證
      auth_token = null;
    }

    String username = jwtUtils.getUsernameFromToken(auth_token);

    logger.info(String.format("Checking authentication for user %s.", username));

    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
      User user = jwtUtils.getUserFromToken(auth_token);
      if (jwtUtils.validateToken(auth_token, user)) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        logger.info(String.format("Authenticated user %s, setting security context", username));
        SecurityContextHolder.getContext().setAuthentication(authentication);
      }
    }
    chain.doFilter(request, response);
  }
}
/**
 * 認(rèn)證失敗處理類,返回401
 * Author: JoeTao
 * createAt: 2018/9/20
 */
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {

  private static final long serialVersionUID = -8970718410437077606L;

  @Override
  public void commence(HttpServletRequest request,
             HttpServletResponse response,
             AuthenticationException authException) throws IOException {
    //驗證為未登陸狀態(tài)會進(jìn)入此方法,認(rèn)證錯誤
    System.out.println("認(rèn)證失?。? + authException.getMessage());
    response.setStatus(200);
    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json; charset=utf-8");
    PrintWriter printWriter = response.getWriter();
    String body = ResultJson.failure(ResultCode.UNAUTHORIZED, authException.getMessage()).toString();
    printWriter.write(body);
    printWriter.flush();
  }
}

因為我們使用的REST API,所以我們認(rèn)為到達(dá)后臺的請求都是正常的,所以返回的HTTP狀態(tài)碼都是200,用接口返回的code來確定請求是否正常。

/**
* 權(quán)限不足處理類,返回403
 * Author: JoeTao
 * createAt: 2018/9/21
 */
@Component("RestAuthenticationAccessDeniedHandler")
public class RestAuthenticationAccessDeniedHandler implements AccessDeniedHandler {
  @Override
  public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
    //登陸狀態(tài)下,權(quán)限不足執(zhí)行該方法
    System.out.println("權(quán)限不足:" + e.getMessage());
    response.setStatus(200);
    response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json; charset=utf-8");
    PrintWriter printWriter = response.getWriter();
    String body = ResultJson.failure(ResultCode.FORBIDDEN, e.getMessage()).toString();
    printWriter.write(body);
    printWriter.flush();
  }
}
/**
 * 登陸身份認(rèn)證
 * Author: JoeTao
 * createAt: 2018/9/14
 */
@Component(value="CustomUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
  private final AuthMapper authMapper;

  public CustomUserDetailsService(AuthMapper authMapper) {
    this.authMapper = authMapper;
  }

  @Override
  public User loadUserByUsername(String name) throws UsernameNotFoundException {
    User user = authMapper.findByUsername(name);
    if (user == null) {
      throw new UsernameNotFoundException(String.format("No user found with username '%s'.", name));
    }
    Role role = authMapper.findRoleByUserId(user.getId());
    user.setRole(role);
    return user;
  }
}

登陸邏輯:

 public ResponseUserToken login(String username, String password) {
    //用戶驗證
    final Authentication authentication = authenticate(username, password);
    //存儲認(rèn)證信息
    SecurityContextHolder.getContext().setAuthentication(authentication);
    //生成token
    final User user = (User) authentication.getPrincipal();
//    User user = (User) userDetailsService.loadUserByUsername(username);
    final String token = jwtTokenUtil.generateAccessToken(user);
    //存儲token
    jwtTokenUtil.putToken(username, token);
    return new ResponseUserToken(token, user);
  }

private Authentication authenticate(String username, String password) {
    try {
      //該方法會去調(diào)用userDetailsService.loadUserByUsername()去驗證用戶名和密碼,如果正確,則存儲該用戶名密碼到“security 的 context中”
      return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    } catch (DisabledException | BadCredentialsException e) {
      throw new CustomException(ResultJson.failure(ResultCode.LOGIN_ERROR, e.getMessage()));
    }
  }

自定義異常:

@Getter
public class CustomException extends RuntimeException{
  private ResultJson resultJson;

  public CustomException(ResultJson resultJson) {
    this.resultJson = resultJson;
  }
}

統(tǒng)一異常處理:

/**
 * 異常處理類
 * controller層異常無法捕獲處理,需要自己處理
 * Created by jt on 2018/8/27.
 */
@RestControllerAdvice
@Slf4j
public class DefaultExceptionHandler {

  /**
   * 處理所有自定義異常
   * @param e
   * @return
   */
  @ExceptionHandler(CustomException.class)
  public ResultJson handleCustomException(CustomException e){
    log.error(e.getResultJson().getMsg().toString());
    return e.getResultJson();
  }
}

所有經(jīng)controller轉(zhuǎn)發(fā)的請求拋出的自定義異常都會被捕獲處理,一般情況下就是返回給調(diào)用方一個json的報錯信息,包含自定義狀態(tài)碼、錯誤信息及補充描述信息。

值得注意的是,在請求到達(dá)controller之前,會被Filter攔截,如果在controller或者之前拋出的異常,自定義的異常處理器是無法處理的,需要自己重新定義一個全局異常處理器或者直接處理。

Filter攔截請求兩次的問題

跨域的post的請求會驗證兩次,get不會。網(wǎng)上的解釋是,post請求第一次是預(yù)檢請求,Request Method: OPTIONS。
解決方法:

在webSecurityConfig里添加

.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

就可以不攔截options請求了。

這里只給出了最主要的代碼,還有controller層的訪問權(quán)限設(shè)置,返回狀態(tài)碼,返回類定義等等。

所有代碼已上傳GitHub,項目地址

到此這篇關(guān)于SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot JWT 登陸鑒權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Java?IO異常如何處理詳析

    Java?IO異常如何處理詳析

    異常就是Java程序在運行過程中出現(xiàn)的錯誤,下面這篇文章主要給大家介紹了關(guān)于Java?IO異常如何處理的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Java使用iText生成PDF的步驟和示例

    Java使用iText生成PDF的步驟和示例

    iText 是一個用于創(chuàng)建和處理 PDF 文檔的開源 Java 庫,iText 主要用于生成 PDF 文件,可以將文本、圖像、表格、列表等內(nèi)容添加到 PDF 中,同時支持對 PDF 進(jìn)行編輯、合并、分割、加密、數(shù)字簽名等操作,本文介紹了Java使用iText生成PDF的步驟和示例
    2024-10-10
  • 親手教你IDEA2020.3創(chuàng)建Javaweb項目的步驟詳解

    親手教你IDEA2020.3創(chuàng)建Javaweb項目的步驟詳解

    這篇文章主要介紹了IDEA2020.3創(chuàng)建Javaweb項目的步驟詳解,本文是小編手把手教你,通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-03-03
  • 詳解Java中的悲觀鎖與樂觀鎖

    詳解Java中的悲觀鎖與樂觀鎖

    樂觀鎖對應(yīng)于生活中樂觀的人總是想著事情往好的方向發(fā)展,悲觀鎖對應(yīng)于生活中悲觀的人總是想著事情往壞的方向發(fā)展.這兩種人各有優(yōu)缺點,不能不以場景而定說一種人好于另外一種人,文中詳細(xì)介紹了悲觀鎖與樂觀鎖,需要的朋友可以參考下
    2021-05-05
  • SpringBoot集成PDFBox實現(xiàn)電子簽章的代碼詳解

    SpringBoot集成PDFBox實現(xiàn)電子簽章的代碼詳解

    Apache PDFBox 是一個開源的 Java 庫,用于處理 PDF 文檔,它提供了一系列強大的功能,包括創(chuàng)建、渲染、拆分、合并、加密、解密 PDF 文件,以及從 PDF 中提取文本和元數(shù)據(jù)等,本文給大家介紹了SpringBoot集成PDFBox實現(xiàn)電子簽章,需要的朋友可以參考下
    2024-09-09
  • mybatis中的test語句失效處理方式

    mybatis中的test語句失效處理方式

    這篇文章主要介紹了mybatis中的test語句失效處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 深入Java分布式計算的使用分析

    深入Java分布式計算的使用分析

    本篇文章對Java分布式計算的使用進(jìn)行了詳細(xì)的介紹。需要的朋友參考下
    2013-05-05
  • 詳解Java中使用泛型實現(xiàn)快速排序算法的方法

    詳解Java中使用泛型實現(xiàn)快速排序算法的方法

    這篇文章主要介紹了Java中使用泛型實現(xiàn)快速排序算法的方法,快速排序的平均時間復(fù)雜度為(n\log n),文中的方法立足于基礎(chǔ)而并沒有考慮優(yōu)化處理,需要的朋友可以參考下
    2016-05-05
  • 從Java的jar文件中讀取數(shù)據(jù)的方法

    從Java的jar文件中讀取數(shù)據(jù)的方法

    這篇文章主要介紹了從Java的jar文件中讀取數(shù)據(jù)的方法,實例分析了java檔案文件的相關(guān)操作技巧,需要的朋友可以參考下
    2015-06-06
  • MyBatis中criteria的or(或查詢)語法說明

    MyBatis中criteria的or(或查詢)語法說明

    這篇文章主要介紹了MyBatis中criteria的or(或查詢)語法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評論

瑞昌市| 彭阳县| 临沧市| 长兴县| 定结县| 石嘴山市| 恩施市| 忻城县| 朔州市| 沙河市| 夏河县| 杭锦旗| 收藏| 海伦市| 乌拉特前旗| 天镇县| 德清县| 河间市| 黄骅市| 贵南县| 乐昌市| 商水县| 东平县| 阜南县| 西青区| 桂平市| 大石桥市| 花莲市| 军事| 阿坝| 邹平县| 黔东| 墨脱县| 渑池县| 密云县| 原阳县| 灌云县| 望谟县| 西藏| 长乐市| 巫山县|