<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringSecurity +Jwt 實(shí)現(xiàn)權(quán)限管理

          共 29907字,需瀏覽 60分鐘

           ·

          2021-04-03 21:25

          公眾號關(guān)注 “GitHub今日熱榜
          設(shè)為 “星標(biāo)”,帶你挖掘更多開發(fā)神器!






          為了方便,未采用是從數(shù)據(jù)庫讀取的方式。工具類都是別人那偷的(滑稽。


          原理架構(gòu)圖



          demo的項(xiàng)目結(jié)構(gòu)



          • JwtTokenUtil : JwtToken生成的工具類

          • RestAuthenticationEntryPoint : 當(dāng)未登錄或者token失效訪問接口時,自定義的返回結(jié)果

          • RestfulAccessDeniedHandler : 當(dāng)訪問接口沒有權(quán)限時,自定義的返回結(jié)果

          • JwtAuthenticationTokenFilter : JWT登錄授權(quán)過濾器

          • SecurityConfig:SpringSecurity 的配置類

          • User : 實(shí)現(xiàn) UserDetails 接口 ,主要功能是 保存用戶的賬號密碼以及角色列表

          • MyUserDetailsService:實(shí)現(xiàn)了 UserDetailsService 接口的 loadUserByUsername 方法 ,主要是進(jìn)行登錄信息驗(yàn)證,在輸入賬戶密碼進(jìn)行登錄的時候,會進(jìn)入這個類進(jìn)行驗(yàn)證信息。


          由于類比較多,一般建議創(chuàng)建的流程:


          1、創(chuàng)建 JwtTokenUtil

          2、創(chuàng)建 RestAuthenticationEntryPoint 和 RestfulAccessDeniedHandler

          3、創(chuàng)建 MyUserDetailsService

          4、創(chuàng)建 JwtAuthenticationTokenFilter

          5、配置 Security信息 SecurityConfig


          JwtTokenUtil


          import io.jsonwebtoken.Claims;
          import io.jsonwebtoken.Jwts;
          import io.jsonwebtoken.SignatureAlgorithm;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.security.core.userdetails.UserDetails;
          import org.springframework.stereotype.Component;

          import java.util.Date;
          import java.util.HashMap;
          import java.util.Map;

          /**
           * @Description: JwtToken生成的工具類
           * @Author: zlf
           * @Date: 2021/3/30
           */

          @Component
          public class JwtTokenUtil {
              private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
              private static final String CLAIM_KEY_USERNAME = "sub";
              private static final String CLAIM_KEY_CREATED = "created";
              @Value("${jwt.secret}")
              private String secret;
              @Value("${jwt.expiration}")
              private Long expiration;

              /**
               * 根據(jù)負(fù)責(zé)生成JWT的token
               */

              private String generateToken(Map<String, Object> claims) {
                  return Jwts.builder()
                          .setClaims(claims)
                          .setExpiration(generateExpirationDate())
                          .signWith(SignatureAlgorithm.HS512, secret)
                          .compact();
              }

              /**
               * 從token中獲取JWT中的負(fù)載
               */

              private Claims getClaimsFromToken(String token) {
                  Claims claims = null;
                  try {
                      claims = Jwts.parser()
                              .setSigningKey(secret)
                              .parseClaimsJws(token)
                              .getBody();
                  } catch (Exception e) {
                      LOGGER.info("JWT格式驗(yàn)證失敗:{}",token);
                  }
                  return claims;
              }

              /**
               * 生成token的過期時間
               */

              private Date generateExpirationDate() {
                  return new Date(System.currentTimeMillis() + expiration * 1000);
              }

              /**
               * 從token中獲取登錄用戶名
               */

              public String getUserNameFromToken(String token) {
                  String username;
                  try {
                      Claims claims = getClaimsFromToken(token);
                      username = claims.getSubject();
                  } catch (Exception e) {
                      username = null;
                  }
                  return username;
              }

              /**
               * 驗(yàn)證token是否還有效
               *
               * @param token 客戶端傳入的token
               * @param userDetails 從數(shù)據(jù)庫中查詢出來的用戶信息
               */

              public boolean validateToken(String token, UserDetails userDetails) {
                  String username = getUserNameFromToken(token);
                  return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
              }

              /**
               * 判斷token是否已經(jīng)失效
               */

              private boolean isTokenExpired(String token) {
                  Date expiredDate = getExpiredDateFromToken(token);
                  return expiredDate.before(new Date());
              }

              /**
               * 從token中獲取過期時間
               */

              private Date getExpiredDateFromToken(String token) {
                  Claims claims = getClaimsFromToken(token);
                  return claims.getExpiration();
              }

              /**
               * 根據(jù)用戶信息生成token
               */

              public String generateToken(UserDetails userDetails) {
                  Map<String, Object> claims = new HashMap<>();
                  claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
                  claims.put(CLAIM_KEY_CREATED, new Date());
                  return generateToken(claims);
              }

              /**
               * 判斷token是否可以被刷新
               */

              public boolean canRefresh(String token) {
                  return !isTokenExpired(token);
              }

              /**
               * 刷新token
               */

              public String refreshToken(String token) {
                  Claims claims = getClaimsFromToken(token);
                  claims.put(CLAIM_KEY_CREATED, new Date());
                  return generateToken(claims);
              }
          }


          RestAuthenticationEntryPoint 和 RestfulAccessDeniedHandler


          import cn.hutool.json.JSONUtil;
          import com.zlf.Api.CommonResult;
          import org.springframework.security.core.AuthenticationException;
          import org.springframework.security.web.AuthenticationEntryPoint;
          import org.springframework.stereotype.Component;


          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;

          /**
          * @Description: 當(dāng)未登錄或者token失效訪問接口時,自定義的返回結(jié)果
          * @Author: zlf
          * @Date: 2021/3/30
          */

          @Component
          public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
              @Override
              public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                  System.out.println("--- --- 未登錄");
                  response.setCharacterEncoding("UTF-8");
                  response.setContentType("application/json");
                  response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized("失敗")));
                  response.getWriter().flush();
              }
          }


          import cn.hutool.json.JSONUtil;
          import com.zlf.Api.CommonResult;
          import org.springframework.security.access.AccessDeniedException;
          import org.springframework.security.web.access.AccessDeniedHandler;
          import org.springframework.stereotype.Component;

          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;

          /**
          * @Description: 當(dāng)訪問接口沒有權(quán)限時,自定義的返回結(jié)果
          * @Author: zlf
          * @Date: 2021/3/30
          */

          @Component
          public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
              @Override
              public void handle(HttpServletRequest request,
                                 HttpServletResponse response,
                                 AccessDeniedException e)
           throws IOException, ServletException
          {
                  response.setCharacterEncoding("UTF-8");
                  response.setContentType("application/json");
                  response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
                  response.getWriter().flush();
              }
          }


          MyUserDetailsService


          import org.springframework.security.core.GrantedAuthority;
          import org.springframework.security.core.authority.SimpleGrantedAuthority;
          import org.springframework.security.core.userdetails.UserDetails;
          import org.springframework.security.core.userdetails.UserDetailsService;
          import org.springframework.security.core.userdetails.UsernameNotFoundException;
          import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
          import org.springframework.stereotype.Component;

          import java.util.ArrayList;
          import java.util.List;

          /**
           * Created with IntelliJ IDEA.
           *
           * @Auther: zlf
           * @Date: 2021/03/30/23:38
           * @Description:
           */

          @Component
          public class MyUserDetailsService implements UserDetailsService {
              @Override
              public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                // 這邊可以通過username去獲取數(shù)據(jù)庫中的用戶信息 如果沒有拋出異常
                  List<GrantedAuthority> authorityList = new ArrayList<>();
                  /* 此處查詢數(shù)據(jù)庫得到角色權(quán)限列表,這里可以用Redis緩存以增加查詢速度 */
                  System.out.println("--- ---- 判斷");
                  authorityList.add(new SimpleGrantedAuthority("ROLE_USER")); // 角色 需要以 ROLE_ 開頭
                  return new org.springframework.security.core.userdetails.User(username, new BCryptPasswordEncoder().encode("123456"), authorityList);
              }
          }


          JwtAuthenticationTokenFilter


          import com.zlf.utils.JwtTokenUtil;
          import lombok.extern.slf4j.Slf4j;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
          import org.springframework.security.core.GrantedAuthority;
          import org.springframework.security.core.authority.SimpleGrantedAuthority;
          import org.springframework.security.core.context.SecurityContextHolder;
          import org.springframework.security.core.userdetails.UserDetails;
          import org.springframework.security.core.userdetails.UsernameNotFoundException;
          import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
          import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
          import org.springframework.stereotype.Component;
          import org.springframework.web.filter.OncePerRequestFilter;

          import javax.servlet.FilterChain;
          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
          import java.util.ArrayList;
          import java.util.List;


          /**
          * @Description: JWT登錄授權(quán)過濾器
          * @Author: zlf
          * @Date: 2021/3/30
          */

          @Component
          @Slf4j
          public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

              //@Autowired
              //private UserDetailsService userDetailsService;
              @Autowired
              private JwtTokenUtil jwtTokenUtil;
              @Value("${jwt.tokenHeader}")
              private String tokenHeader;
              @Value("${jwt.tokenHead}")
              private String tokenHead;

              @Autowired
              private MyUserDetailsService userDetailsService;

              @Override
              protected void doFilterInternal(HttpServletRequest request,
                                              HttpServletResponse response,
                                              FilterChain chain)
           throws ServletException, IOException
          {
                  String authHeader = request.getHeader(this.tokenHeader);
                  if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
                      String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
                      String username = jwtTokenUtil.getUserNameFromToken(authToken);
                      log.info("checking username:{}", username);
                      if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                          UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                          if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                              UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, new BCryptPasswordEncoder().encode("123456"), userDetails.getAuthorities());
                              authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                              log.info("authenticated user:{}", username);
                              SecurityContextHolder.getContext().setAuthentication(authentication);
                          }
                      }
                  }
                  chain.doFilter(request, response);
              }

          // public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          // List<GrantedAuthority> authorityList = new ArrayList<>();
          // /* 此處查詢數(shù)據(jù)庫得到角色權(quán)限列表,這里可以用Redis緩存以增加查詢速度 */
          // authorityList.add(new SimpleGrantedAuthority("ROLE_USER")); // 角色 需要以 ROLE_ 開頭
          // return new org.springframework.security.core.userdetails.User(username, "123456", true, true,
          // true, true, authorityList);
          // }
          }


          SecurityConfig


          import com.zlf.component.JwtAuthenticationTokenFilter;
          import com.zlf.component.MyUserDetailsService;
          import com.zlf.component.RestAuthenticationEntryPoint;
          import com.zlf.component.RestfulAccessDeniedHandler;
          import com.zlf.entity.model.Admin;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Bean;
          import org.springframework.http.HttpMethod;
          import org.springframework.security.authentication.AuthenticationManager;
          import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
          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.config.http.SessionCreationPolicy;
          import org.springframework.security.core.userdetails.UserDetailsService;
          import org.springframework.security.core.userdetails.UsernameNotFoundException;
          import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
          import org.springframework.security.crypto.password.PasswordEncoder;
          import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

          /**
           * Created with IntelliJ IDEA.
           *
           * @Auther: zlf
           * @Date: 2021/03/30/20:45
           * @Description:
           */

          @EnableWebSecurity
          public class SecurityConfig extends WebSecurityConfigurerAdapter {



              @Autowired
              private MyUserDetailsService userDetailsService;

              @Autowired
              private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
              @Autowired
              private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

              @Autowired
              private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;


              @Override
              protected void configure(HttpSecurity httpSecurity) throws Exception {
                  httpSecurity.csrf()// 由于使用的是JWT,我們這里不需要csrf
                          .disable()
                          .sessionManagement()// 基于token,所以不需要session
                          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                          .and()
                          .authorizeRequests()
                          .antMatchers(HttpMethod.GET, // 允許對于網(wǎng)站靜態(tài)資源的無授權(quán)訪問
                                  "/",
                                  "/*.html",
                                  "/favicon.ico",
                                  "/**/*.html",
                                  "/**/*.css",
                                  "/**/*.js",
                                  "/swagger-resources/**",
                                  "/v2/api-docs/**"
                          )
                          .permitAll()
                          .antMatchers("/admin/login", "/register")// 對登錄注冊要允許匿名訪問
                          .permitAll()
                          .antMatchers(HttpMethod.OPTIONS)//跨域請求會先進(jìn)行一次options請求
                          .permitAll()
          // .antMatchers("/**")//測試時全部運(yùn)行訪問
          // .permitAll()
                          .anyRequest()// 除上面外的所有請求全部需要鑒權(quán)認(rèn)證
                          .authenticated();
                  // 禁用緩存
                  httpSecurity.headers().cacheControl();
                  // 添加JWT filter
                  httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
                  //添加自定義未授權(quán)和未登錄結(jié)果返回
                  httpSecurity.exceptionHandling()
                          .accessDeniedHandler(restfulAccessDeniedHandler)
                          .authenticationEntryPoint(restAuthenticationEntryPoint);
              }

              @Override
              protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                  auth.userDetailsService(userDetailsService)
                          .passwordEncoder(new BCryptPasswordEncoder());
              }

          // @Bean
          // public PasswordEncoder passwordEncoder() {
          // return new BCryptPasswordEncoder();
          // }



          // @Bean
          // public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
          // return new JwtAuthenticationTokenFilter();
          // }

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


          Controller


          import com.zlf.entity.User;
          import com.zlf.utils.JwtTokenUtil;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.security.access.prepost.PreAuthorize;
          import org.springframework.security.core.userdetails.UserDetails;
          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.PostMapping;
          import org.springframework.web.bind.annotation.RequestBody;
          import org.springframework.web.bind.annotation.RestController;

          import javax.servlet.http.HttpServletRequest;
          import java.util.HashMap;
          import java.util.Map;

          /**
           * Created with IntelliJ IDEA.
           *
           * @Auther: zlf
           * @Date: 2021/03/30/23:15
           * @Description:
           */

          @RestController
          public class LoginController {

              @Autowired
              private JwtTokenUtil jwtTokenUtil;

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

              @PostMapping("/admin/login")
              public String login(@RequestBody User user, HttpServletRequest request) {
                  /* 在這里驗(yàn)證用戶名和密碼,驗(yàn)證成功則生成token返回 */
                  System.out.println("--- -- -login");
                  System.out.println(user);
                  return  tokenHead+" "+jwtTokenUtil.generateToken(user); // 生成 Token,返回給客戶端
              }

              @PreAuthorize("hasAnyRole('USER')") // 對單個方法進(jìn)行權(quán)限控制
              @GetMapping("/me")
              public String me() {
                  // 從上下文中獲取 UserDetails
                  UserDetails userDetails = (UserDetails) org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                  return userDetails.getUsername() + "," + userDetails.getPassword();
              }
          }


          Postman 測試



          我們將登錄獲取到的 Token ,放到請求頭中。



          最后放出 JWT key的配置


          # 自定義jwt key
          jwt:
            tokenHeader: Authorization #JWT存儲的請求頭
            secret: mySecret #JWT加解密使用的密鑰
            expiration: 604800 #JWT的超期限時間(60*60*24)
            tokenHead: Bearer #JWT負(fù)載中拿到開頭


          依賴


          <!--Hutool Java工具包-->
                  <dependency>
                      <groupId>cn.hutool</groupId>
                      <artifactId>hutool-all</artifactId>
                      <version>4.5.7</version>
                  </dependency>
                  <!--JWT(Json Web Token)登錄支持-->
                  <dependency>
                      <groupId>io.jsonwebtoken</groupId>
                      <artifactId>jjwt</artifactId>
                      <version>0.9.0</version>
                  </dependency>
               <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>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                      <optional>true</optional>
                  </dependency>





          出處:https://blog.csdn.net/xiuyuandashen/article/details/115354629








          關(guān)注GitHub今日熱榜,專注挖掘好用的開發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開發(fā)者成長!







          點(diǎn)個在看 你最好看



          瀏覽 61
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美 日韩 人妻 | 无码内射网 | 欧美淫秽视频免费看 | 黄色无码视频在线免费观看 | 亚洲乱码精品 |