<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      筆記

      1. 認證

      1.1 登陸校驗流程

      1.2 原理初探

      1.2.1 SpringSecurity完整流程

      SpringSecurity的原理其實就是一個過濾器鏈,內部包含了提供各種功能的過濾器。

      圖中只展示了核心過濾器,其它的非核心過濾器并沒有在圖中展示。

      UsernamePasswordAuthenticationFilter:負責處理我們在登陸頁面填寫了用戶名密碼后的登陸請求。入門案例的認證工作主要有它負責。

      ExceptionTranslationFilter:處理過濾器鏈中拋出的任何AccessDeniedException和AuthenticationException 。

      FilterSecurityInterceptor:負責權限校驗的過濾器。

      1.2.2 認證流程詳解

      概念速查:

      Authentication接口: 它的實現類,表示當前訪問系統的用戶,封裝了用戶相關信息。

      AuthenticationManager接口:定義了認證Authentication的方法

      UserDetailsService接口:加載用戶特定數據的核心接口。里面定義了一個根據用戶名查詢用戶信息的方法。

      UserDetails接口:提供核心用戶信息。通過UserDetailsService根據用戶名獲取處理的用戶信息要封裝成UserDetails對象返回。然后將這些信息封裝到Authentication對象中。

      1.3 解決問題(思路分析)

      登錄

        ①自定義登錄接口

          調用ProviderManager的方法進行認證 如果認證通過生成jwt

          把用戶信息存入redis中

        ②自定義UserDetailsService

          在這個實現類中去查詢數據庫

      校驗:

        ①定義Jwt認證過濾器

          獲取token

          解析token獲取其中的userid

          從redis中獲取用戶信息

          存入SecurityContextHolder

      2. 授權

      2.1 授權基本流程

      在SpringSecurity中,會使用默認的FilterSecurityInterceptor來進行權限校驗。在FilterSecurityInterceptor中會從SecurityContextHolder獲取其中的Authentication,然后獲取其中的權限信息。當前用戶是否擁有訪問當前資源所需的權限。

      所以在項目中只需要把當前登錄用戶的權限信息也存入Authentication。

      然后設置資源所需要的權限即可。

      2.2 授權實現

      2.2.1 限制訪問資源所需權限

      SpringSecurity提供了基于注解的權限控制方案,這也是項目中主要采用的方式。我們可以使用注解去指定訪問對應的資源所需的權限。

      2.2.2 封裝權限信息

      在查詢出用戶后還要獲取對應的權限信息,封裝到UserDetails中返回。

      2.2.3 從數據庫查詢權限信息(RBAC權限模型

      RBAC權限模型(Role-Based Access Control)即:基于角色的權限控制。這是目前最常被開發者使用也是相對易用、通用權限模型。

      代碼

       0. 目錄

       

      1. 其他

      SecurityDemo1Application

      package com.bijian.security_demo1;
      
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      @MapperScan("com.bijian.security_demo1.mapper")
      public class SecurityDemo1Application {
      
          public static void main(String[] args) {
              SpringApplication.run(SecurityDemo1Application.class, args);
          }
      
      }
      View Code

      application.yml

      spring:
        datasource:
          type: com.zaxxer.hikari.HikariDataSource
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/springsecurity?serverTimezone=UTC
          username: root
          password: 123456
          #redis
        redis:
          timeout: 10000ms
          host: localhost
          port: 6379
          database: 0
          lettuce:
            pool:
              max-active: 1024
              max-wait: 10000ms
              max-idle: 200
              min-idle: 5
      
      mybatis-plus:
        mapper-locations: classpath*:/mapper/**/*.xml
      
      server:
        port: 8888
      View Code

      pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.5.6</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
          <groupId>com.bijian</groupId>
          <artifactId>security_demo1</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <name>security_demo1</name>
          <description>Demo project for Spring Boot</description>
          <properties>
              <java.version>1.8</java.version>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-devtools</artifactId>
                  <scope>runtime</scope>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
              <!--SpringSecurity引入-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-security</artifactId>
              </dependency>
              <!--redis依賴-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
              </dependency>
              <!--fastjson依賴-->
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>fastjson</artifactId>
                  <version>1.2.33</version>
              </dependency>
              <!--jwt依賴-->
              <dependency>
                  <groupId>io.jsonwebtoken</groupId>
                  <artifactId>jjwt</artifactId>
                  <version>0.9.1</version>
              </dependency>
              <!--引入MybatisPuls和mysql驅動的依賴-->
              <dependency>
                  <groupId>com.baomidou</groupId>
                  <artifactId>mybatis-plus-boot-starter</artifactId>
                  <version>3.4.0</version>
              </dependency>
              <dependency>
                  <groupId>com.baomidou</groupId>
                  <artifactId>mybatis-plus-generator</artifactId>
                  <version>3.5.2</version>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
              </dependency>
              <!--redis依賴-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
              </dependency>
              <!--commons-pool2依賴-->
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-pool2</artifactId>
              </dependency>
          </dependencies>
      
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                      <configuration>
                          <excludes>
                              <exclude>
                                  <groupId>org.projectlombok</groupId>
                                  <artifactId>lombok</artifactId>
                              </exclude>
                          </excludes>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      
      </project>
      View Code

      2. config

      CorsConfig

      package com.bijian.security_demo1.config;
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.CorsRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      @Configuration
      public class CorsConfig implements WebMvcConfigurer {
          @Override
          public void addCorsMappings(CorsRegistry registry) {
              // 設置允許跨域的路徑
              registry.addMapping("/**")
                      // 設置允許跨域請求的域名
                      .allowedOriginPatterns("*")
                      // 是否允許cookie
                      .allowCredentials(true)
                      // 設置允許的請求方式
                      .allowedMethods("GET", "POST", "DELETE", "PUT")
                      // 設置允許的header屬性
                      .allowedHeaders("*")
                      // 跨域允許時間
                      .maxAge(3600);
          }
      }
      View Code

      RedisConfig

      package com.bijian.security_demo1.config;
      
      import com.bijian.security_demo1.utils.FastJsonRedisSerializer;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.connection.RedisConnectionFactory;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.serializer.StringRedisSerializer;
      
      @Configuration
      public class RedisConfig {
      
          @Bean
          @SuppressWarnings(value = { "unchecked", "rawtypes" })
          public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
          {
              RedisTemplate<Object, Object> template = new RedisTemplate<>();
              template.setConnectionFactory(connectionFactory);
      
              FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
      
              // 使用StringRedisSerializer來序列化和反序列化redis的key值
              template.setKeySerializer(new StringRedisSerializer());
              template.setValueSerializer(serializer);
      
              // Hash的key也采用StringRedisSerializer的序列化方式
              template.setHashKeySerializer(new StringRedisSerializer());
              template.setHashValueSerializer(serializer);
      
              template.afterPropertiesSet();
              return template;
          }
      }
      View Code

      SecurityConfig

      package com.bijian.security_demo1.config;
      
      import com.bijian.security_demo1.filter.JwtAuthenticationTokenFilter;
      import com.bijian.security_demo1.handler.AccessDeniedHandlerImpl;
      import com.bijian.security_demo1.handler.AuthenticationEntryPointImpl;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.authentication.AuthenticationManager;
      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.WebSecurityConfigurerAdapter;
      import org.springframework.security.config.http.SessionCreationPolicy;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      import org.springframework.security.crypto.password.PasswordEncoder;
      import org.springframework.security.web.AuthenticationEntryPoint;
      import org.springframework.security.web.access.AccessDeniedHandler;
      import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
      
      @Configuration
      @EnableGlobalMethodSecurity(prePostEnabled = true) // 使用注解去指定訪問對應的資源所需的權限
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
          @Autowired
          JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
          @Autowired
          private AuthenticationEntryPoint authenticationEntryPoint;
          @Autowired
          private AccessDeniedHandler accessDeniedHandler;
      
          @Bean // 創建BCryptPasswordEncoder注入容器
          public PasswordEncoder passwordEncoder(){
              return new BCryptPasswordEncoder();
          }
      
          @Bean
          @Override
          public AuthenticationManager authenticationManagerBean() throws Exception {
              return super.authenticationManagerBean();
          }
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                      //關閉csrf
                      .csrf().disable()
                      //不通過Session獲取SecurityContext
                      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                      .and()
                      .authorizeRequests()
                      // 對于登錄接口 允許匿名訪問
                      .antMatchers("/user/login").anonymous()
                      .antMatchers("/testCors").hasAnyAuthority("system:dept:list")
                      // 除上面外的所有請求全部需要鑒權認證
                      .anyRequest().authenticated();
              // 添加過濾器
              http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
              // 配置異常處理器
              http.exceptionHandling()
                      //配置認證失敗處理
                      .authenticationEntryPoint(authenticationEntryPoint)
                      //配置授權失敗處理
                      .accessDeniedHandler(accessDeniedHandler);
      
              // 允許跨域
              http.cors();
          }
      }

      3. controller

      HelloController

      package com.bijian.security_demo1.controller;
      
      import com.bijian.security_demo1.demain.ResponseResult;
      import org.springframework.security.access.prepost.PreAuthorize;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class HelloController {
      
          @RequestMapping("/hello")
          @PreAuthorize("hasAuthority('system:dept:list111')")
      //    @PreAuthorize("@ex.hasAuthority('system:dept:list')")
          public String hello(){
              return "hello";
          }
      
          @RequestMapping("/testCors")
          public ResponseResult testCors(){
              return new ResponseResult(200,"testCors");
          }
      }

      LoginController

      package com.bijian.security_demo1.controller;
      
      
      import com.bijian.security_demo1.demain.ResponseResult;
      import com.bijian.security_demo1.demain.User;
      import com.bijian.security_demo1.service.LoginService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class LoginController {
          @Autowired
          private LoginService loginService;
      
          @PostMapping("/user/login")
          public ResponseResult login(@RequestBody User user){
              return loginService.login(user);
          }
      
          @RequestMapping("/user/logout")
          public ResponseResult logout(){
              return loginService.logout();
          }
      }

      4. demain

      LoginUser

      package com.bijian.security_demo1.demain;
      
      import com.alibaba.fastjson.annotation.JSONField;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      import org.springframework.security.core.GrantedAuthority;
      import org.springframework.security.core.authority.SimpleGrantedAuthority;
      import org.springframework.security.core.userdetails.UserDetails;
      
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.List;
      import java.util.stream.Collectors;
      
      @Data
      @NoArgsConstructor
      //@AllArgsConstructor
      public class LoginUser implements UserDetails {
      
          private User user;
      
          public LoginUser(User user, List<String> permissions) {
              this.user = user;
              this.permissions = permissions;
          }
      
          //存儲權限信息
          private List<String> permissions;
      
          @JSONField(serialize = false)
          private List<SimpleGrantedAuthority> authorities;
      
          @Override
          public Collection<? extends GrantedAuthority> getAuthorities() {
              if (authorities!=null){
                  return  authorities;
              }
              //把permissions中字符串類型的權限信息轉換成GrantedAuthority對象存入authorities中
      //        authorities = new ArrayList<>();
      //        for (String permission:permissions) {
      //            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
      //            authorities.add(authority);
      //        }
              authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
              return authorities;
          }
      
          @Override
          public String getPassword() {
              return user.getPassword();
          }
      
          @Override
          public String getUsername() {
              return user.getUserName();
          }
      
          @Override
          public boolean isAccountNonExpired() {
              return true;
          }
      
          @Override
          public boolean isAccountNonLocked() {
              return true;
          }
      
          @Override
          public boolean isCredentialsNonExpired() {
              return true;
          }
      
          @Override
          public boolean isEnabled() {
              return true;
          }
      }
      View Code

      Menu

      package com.bijian.security_demo1.demain;
      
      import com.baomidou.mybatisplus.annotation.TableId;
      import com.baomidou.mybatisplus.annotation.TableName;
      import com.fasterxml.jackson.annotation.JsonInclude;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.io.Serializable;
      import java.util.Date;
      
      /**
       * 菜單表(Menu)實體類
       */
      @TableName(value="sys_menu")
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @JsonInclude(JsonInclude.Include.NON_NULL)
      public class Menu implements Serializable {
          private static final long serialVersionUID = -54979041104113736L;
      
          @TableId
          private Long id;
          /**
           * 菜單名
           */
          private String menuName;
          /**
           * 路由地址
           */
          private String path;
          /**
           * 組件路徑
           */
          private String component;
          /**
           * 菜單狀態(0顯示 1隱藏)
           */
          private String visible;
          /**
           * 菜單狀態(0正常 1停用)
           */
          private String status;
          /**
           * 權限標識
           */
          private String perms;
          /**
           * 菜單圖標
           */
          private String icon;
      
          private Long createBy;
      
          private Date createTime;
      
          private Long updateBy;
      
          private Date updateTime;
          /**
           * 是否刪除(0未刪除 1已刪除)
           */
          private Integer delFlag;
          /**
           * 備注
           */
          private String remark;
      }
      View Code

      ResponseResult

      package com.bijian.security_demo1.demain;
      
      import com.fasterxml.jackson.annotation.JsonInclude;
      
      @JsonInclude(JsonInclude.Include.NON_NULL)
      public class ResponseResult<T> {
          /**
           * 狀態碼
           */
          private Integer code;
          /**
           * 提示信息,如果有錯誤時,前端可以獲取該字段進行提示
           */
          private String msg;
          /**
           * 查詢到的結果數據,
           */
          private T data;
      
          public ResponseResult(Integer code, String msg) {
              this.code = code;
              this.msg = msg;
          }
      
          public ResponseResult(Integer code, T data) {
              this.code = code;
              this.data = data;
          }
      
          public Integer getCode() {
              return code;
          }
      
          public void setCode(Integer code) {
              this.code = code;
          }
      
          public String getMsg() {
              return msg;
          }
      
          public void setMsg(String msg) {
              this.msg = msg;
          }
      
          public T getData() {
              return data;
          }
      
          public void setData(T data) {
              this.data = data;
          }
      
          public ResponseResult(Integer code, String msg, T data) {
              this.code = code;
              this.msg = msg;
              this.data = data;
          }
      }
      View Code

      User

      package com.bijian.security_demo1.demain;
      
      import com.baomidou.mybatisplus.annotation.TableId;
      import com.baomidou.mybatisplus.annotation.TableName;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.io.Serializable;
      import java.util.Date;
      
      
      /**
       * 用戶表(User)實體類
       */
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @TableName(value = "sys_user")
      public class User implements Serializable {
          private static final long serialVersionUID = -40356785423868312L;
      
          /**
           * 主鍵
           */
          @TableId
          private Long id;
          /**
           * 用戶名
           */
          private String userName;
          /**
           * 昵稱
           */
          private String nickName;
          /**
           * 密碼
           */
          private String password;
          /**
           * 賬號狀態(0正常 1停用)
           */
          private String status;
          /**
           * 郵箱
           */
          private String email;
          /**
           * 手機號
           */
          private String phonenumber;
          /**
           * 用戶性別(0男,1女,2未知)
           */
          private String sex;
          /**
           * 頭像
           */
          private String avatar;
          /**
           * 用戶類型(0管理員,1普通用戶)
           */
          private String userType;
          /**
           * 創建人的用戶id
           */
          private Long createBy;
          /**
           * 創建時間
           */
          private Date createTime;
          /**
           * 更新人
           */
          private Long updateBy;
          /**
           * 更新時間
           */
          private Date updateTime;
          /**
           * 刪除標志(0代表未刪除,1代表已刪除)
           */
          private Integer delFlag;
      }
      View Code

      5. expression

      BJExpressionRoot

      package com.bijian.security_demo1.expression;
      
      import com.bijian.security_demo1.demain.LoginUser;
      import org.springframework.security.core.Authentication;
      import org.springframework.security.core.context.SecurityContextHolder;
      import org.springframework.stereotype.Component;
      
      import java.util.List;
      
      @Component("ex")
      public class BJExpressionRoot {
          public boolean hasAuthority(String authority){
              //獲取當前用戶的權限
              Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
              LoginUser loginUser = (LoginUser) authentication.getPrincipal();
              List<String> permissions = loginUser.getPermissions();
              //判斷用戶權限集合中是否存在authority
              return permissions.contains(authority);
          }
      }

      6. filter

      JwtAuthenticationTokenFilter

      package com.bijian.security_demo1.filter;
      
      import com.bijian.security_demo1.demain.LoginUser;
      import com.bijian.security_demo1.utils.JwtUtil;
      import com.bijian.security_demo1.utils.RedisCache;
      import io.jsonwebtoken.Claims;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
      import org.springframework.security.core.context.SecurityContextHolder;
      import org.springframework.stereotype.Component;
      import org.springframework.util.StringUtils;
      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.Objects;
      
      /**
       * 獲取請求頭中的token,對token進行解析取出其中的userid
       * 使用userid去redis中獲取對應的LoginUser對象
       * 然后封裝Authentication對象存入SecurityContextHolder
       */
      @Component
      public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
          @Autowired
          private RedisCache redisCache;
      
          @Override
          protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
              //獲取token
              String token = request.getHeader("token");
              if(!StringUtils.hasText(token)){
                  //放行
                  filterChain.doFilter(request,response);
                  return;
              }
              String userid;
              //解析token
              try {
                  Claims claims = JwtUtil.parseJWT(token);
                  userid = claims.getSubject();
              } catch (Exception e) {
                  e.printStackTrace();
                  throw new RuntimeException("token非法");
              }
              //從redis中獲取用戶信息
              String redisKey = "login"+userid;
              LoginUser loginUser = redisCache.getCacheObject(redisKey);
              if(Objects.isNull(loginUser)){
                  throw new RuntimeException("用戶未登錄");
              }
              //存入SecurityContextHolder
              //TODO 獲取權限信息封裝到Authentication中
              UsernamePasswordAuthenticationToken authenticationToken =
                      new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
              SecurityContextHolder.getContext().setAuthentication(authenticationToken);
              //放行
              filterChain.doFilter(request,response);
          }
      }

      7. handler

      AccessDeniedHandlerImpl

      package com.bijian.security_demo1.handler;
      
      import com.alibaba.fastjson.JSON;
      import com.bijian.security_demo1.demain.ResponseResult;
      import com.bijian.security_demo1.utils.WebUtils;
      import org.springframework.http.HttpStatus;
      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;
      
      @Component
      public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
          @Override
          public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
              ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "您的權限不足");
              String json = JSON.toJSONString(result);
              //處理異常
              WebUtils.renderString(httpServletResponse,json);
          }
      }

      AuthenticationEntryPointImpl

      package com.bijian.security_demo1.handler;
      
      import com.alibaba.fastjson.JSON;
      import com.bijian.security_demo1.demain.ResponseResult;
      import com.bijian.security_demo1.utils.WebUtils;
      import org.springframework.http.HttpStatus;
      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;
      
      @Component
      public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
      
          @Override
          public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
              ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "用戶認證失敗請查詢登錄");
              String json = JSON.toJSONString(result);
              //處理異常
              WebUtils.renderString(httpServletResponse,json);
          }
      }

      8. java/mapper

      MenuMapper

      package com.bijian.security_demo1.mapper;
      
      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.bijian.security_demo1.demain.Menu;
      import org.springframework.stereotype.Repository;
      
      import java.util.List;
      
      @Repository
      public interface MenuMapper extends BaseMapper<Menu> {
          List<String> selectPermsByUserId(Long id);
      }

      UserMapper

      package com.bijian.security_demo1.mapper;
      
      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.bijian.security_demo1.demain.User;
      import org.springframework.stereotype.Repository;
      
      import java.util.List;
      
      @Repository
      public interface UserMapper extends BaseMapper<User> {
          // 根據用戶id去查詢到其所對應的權限信息
          List<String> selectPermsByUserId(Long id);
      
      }

      9. service

      LoginService

      package com.bijian.security_demo1.service;
      
      import com.bijian.security_demo1.demain.ResponseResult;
      import com.bijian.security_demo1.demain.User;
      
      public interface LoginService {
      
          ResponseResult login(User user);
      
          ResponseResult logout();
      }

      LoginServiceImpl

      package com.bijian.security_demo1.service.impl;
      
      import com.bijian.security_demo1.demain.LoginUser;
      import com.bijian.security_demo1.demain.ResponseResult;
      import com.bijian.security_demo1.demain.User;
      import com.bijian.security_demo1.service.LoginService;
      import com.bijian.security_demo1.utils.JwtUtil;
      import com.bijian.security_demo1.utils.RedisCache;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.security.authentication.AuthenticationManager;
      import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
      import org.springframework.security.core.Authentication;
      import org.springframework.security.core.context.SecurityContextHolder;
      import org.springframework.stereotype.Service;
      
      import java.util.HashMap;
      import java.util.Map;
      import java.util.Objects;
      
      @Service
      public class LoginServiceImpl implements LoginService {
          @Autowired
          private AuthenticationManager authenticationManager;
          @Autowired
          private RedisCache redisCache;
      
          @Override
          public ResponseResult login(User user) {
              // AuthenticationManager  authenticate 進行用戶認證
              UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
              Authentication authenticate = authenticationManager.authenticate(authenticationToken);
              // 如果認證沒通過,給出對應的提示
              if(Objects.isNull(authenticate)){
                  throw new RuntimeException("登錄失敗");
              }
              // 如果認證通過了,使用userid生成一個jwt jwt存入ResponseResult返回
              LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
              String userid = loginUser.getUser().getId().toString();
              String jwt = JwtUtil.createJWT(userid);
              Map<String,String> map = new HashMap<>();
              map.put("token",jwt);
              //把完整的用戶信息存入Redis userid作為key
              redisCache.setCacheObject("login"+userid,loginUser);
              return new ResponseResult(200,"登錄成功",map);
          }
      
          @Override  // 需要定義一個登陸接口,然后獲取SecurityContextHolder中的認證信息,刪除redis中對應的數據即可
          public ResponseResult logout() {
              // 獲取SecurityContextHolder中的用戶id
              Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
              LoginUser loginUser = (LoginUser) authentication.getPrincipal();
              Long userid = loginUser.getUser().getId();
              // 刪除Redis中的值
              redisCache.deleteObject("login" + userid);
              return new ResponseResult(200,"退出成功");
          }
      }

      UserDetailsServiceImpl

      package com.bijian.security_demo1.service.impl;
      
      
      import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
      import com.bijian.security_demo1.demain.LoginUser;
      import com.bijian.security_demo1.demain.User;
      import com.bijian.security_demo1.mapper.MenuMapper;
      import com.bijian.security_demo1.mapper.UserMapper;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.security.core.userdetails.UserDetails;
      import org.springframework.security.core.userdetails.UserDetailsService;
      import org.springframework.security.core.userdetails.UsernameNotFoundException;
      import org.springframework.stereotype.Service;
      
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      import java.util.Objects;
      
      @Service
      public class UserDetailsServiceImpl implements UserDetailsService {
          @Autowired
          private UserMapper userMapper;
          @Autowired
          private MenuMapper menuMapper;
      
          @Override
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
              //根據用戶名查詢用戶信息
              User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserName, username));
              if(Objects.isNull(user)){
                  throw new RuntimeException("用戶名或密碼錯誤");
              }
              //TODO 根據用戶查詢權限信息 添加到LoginUser中
      //        List<String> list = new ArrayList<>(Arrays.asList("test","admin"));
              List<String> list = menuMapper.selectPermsByUserId(user.getId());
              return new LoginUser(user,list);
          }
      }

      10. utils

      FastJsonRedisSerializer

      package com.bijian.security_demo1.utils;
      
      import com.alibaba.fastjson.JSON;
      import com.alibaba.fastjson.serializer.SerializerFeature;
      import com.fasterxml.jackson.databind.JavaType;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.fasterxml.jackson.databind.type.TypeFactory;
      import org.springframework.data.redis.serializer.RedisSerializer;
      import org.springframework.data.redis.serializer.SerializationException;
      import com.alibaba.fastjson.parser.ParserConfig;
      import org.springframework.util.Assert;
      import java.nio.charset.Charset;
      
      /**
       * Redis使用FastJson序列化
       * 
       * @author sg
       */
      public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
      {
      
          public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
      
          private Class<T> clazz;
      
          static
          {
              ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
          }
      
          public FastJsonRedisSerializer(Class<T> clazz)
          {
              super();
              this.clazz = clazz;
          }
      
          @Override
          public byte[] serialize(T t) throws SerializationException
          {
              if (t == null)
              {
                  return new byte[0];
              }
              return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
          }
      
          @Override
          public T deserialize(byte[] bytes) throws SerializationException
          {
              if (bytes == null || bytes.length <= 0)
              {
                  return null;
              }
              String str = new String(bytes, DEFAULT_CHARSET);
      
              return JSON.parseObject(str, clazz);
          }
      
      
          protected JavaType getJavaType(Class<?> clazz)
          {
              return TypeFactory.defaultInstance().constructType(clazz);
          }
      }
      View Code

      JwtUtil

      package com.bijian.security_demo1.utils;
      
      import io.jsonwebtoken.Claims;
      import io.jsonwebtoken.JwtBuilder;
      import io.jsonwebtoken.Jwts;
      import io.jsonwebtoken.SignatureAlgorithm;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      
      import javax.crypto.SecretKey;
      import javax.crypto.spec.SecretKeySpec;
      import java.util.Base64;
      import java.util.Date;
      import java.util.UUID;
      
      /**
       * JWT工具類
       */
      public class JwtUtil {
      
          //有效期為
          public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一個小時
          //設置秘鑰明文
          public static final String JWT_KEY = "sangeng";
      
          public static String getUUID(){
              String token = UUID.randomUUID().toString().replaceAll("-", "");
              return token;
          }
      
          /**
           * 生成jtw
           * @param subject token中要存放的數據(json格式)
           * @return
           */
          public static String createJWT(String subject) {
              JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 設置過期時間
              return builder.compact();
          }
      
          /**
           * 生成jtw
           * @param subject token中要存放的數據(json格式)
           * @param ttlMillis token超時時間
           * @return
           */
          public static String createJWT(String subject, Long ttlMillis) {
              JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 設置過期時間
              return builder.compact();
          }
      
          private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
              SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
              SecretKey secretKey = generalKey();
              long nowMillis = System.currentTimeMillis();
              Date now = new Date(nowMillis);
              if(ttlMillis==null){
                  ttlMillis=JwtUtil.JWT_TTL;
              }
              long expMillis = nowMillis + ttlMillis;
              Date expDate = new Date(expMillis);
              return Jwts.builder()
                      .setId(uuid)              //唯一的ID
                      .setSubject(subject)   // 主題  可以是JSON數據
                      .setIssuer("sg")     // 簽發者
                      .setIssuedAt(now)      // 簽發時間
                      .signWith(signatureAlgorithm, secretKey) //使用HS256對稱加密算法簽名, 第二個參數為秘鑰
                      .setExpiration(expDate);
          }
      
          /**
           * 創建token
           * @param id
           * @param subject
           * @param ttlMillis
           * @return
           */
          public static String createJWT(String id, String subject, Long ttlMillis) {
              JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 設置過期時間
              return builder.compact();
          }
      
          public static void main(String[] args) throws Exception {
      //        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjMmRkMmRmNDJkYjQ0N2JjYjQ1NjNkMDYxNzk3OTE2NiIsInN1YiI6IjEiLCJpc3MiOiJzZyIsImlhdCI6MTY4OTkzMDQ0OCwiZXhwIjoxNjg5OTM0MDQ4fQ.j-pP17z0KWvOPCqAxZWHE_Ltsx-lIydo4hwgF43B7KY";
      //        Claims claims = parseJWT(token);
      //        System.out.println(claims);
      //        System.out.println(claims.getSubject());
      
              BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
      //        String encode = passwordEncoder.encode("123456");
              String encode = passwordEncoder.encode("654321");
              System.out.println(encode);
          }
      
          /**
           * 生成加密后的秘鑰 secretKey
           * @return
           */
          public static SecretKey generalKey() {
              byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
              SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
              return key;
          }
      
          /**
           * 解析
           *
           * @param jwt
           * @return
           * @throws Exception
           */
          public static Claims parseJWT(String jwt) throws Exception {
              SecretKey secretKey = generalKey();
              return Jwts.parser()
                      .setSigningKey(secretKey)
                      .parseClaimsJws(jwt)
                      .getBody();
          }
      }
      View Code

      RedisCache

      package com.bijian.security_demo1.utils;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.data.redis.core.BoundSetOperations;
      import org.springframework.data.redis.core.HashOperations;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.core.ValueOperations;
      import org.springframework.stereotype.Component;
      
      import java.util.*;
      import java.util.concurrent.TimeUnit;
      
      @Component
      @SuppressWarnings(value = { "unchecked", "rawtypes" })
      public class RedisCache
      {
          @Autowired
          public RedisTemplate redisTemplate;
      
          /**
           * 緩存基本的對象,Integer、String、實體類等
           *
           * @param key 緩存的鍵值
           * @param value 緩存的值
           */
          public <T> void setCacheObject(final String key, final T value)
          {
              redisTemplate.opsForValue().set(key, value);
          }
      
          /**
           * 緩存基本的對象,Integer、String、實體類等
           *
           * @param key 緩存的鍵值
           * @param value 緩存的值
           * @param timeout 時間
           * @param timeUnit 時間顆粒度
           */
          public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
          {
              redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
          }
      
          /**
           * 設置有效時間
           *
           * @param key Redis鍵
           * @param timeout 超時時間
           * @return true=設置成功;false=設置失敗
           */
          public boolean expire(final String key, final long timeout)
          {
              return expire(key, timeout, TimeUnit.SECONDS);
          }
      
          /**
           * 設置有效時間
           *
           * @param key Redis鍵
           * @param timeout 超時時間
           * @param unit 時間單位
           * @return true=設置成功;false=設置失敗
           */
          public boolean expire(final String key, final long timeout, final TimeUnit unit)
          {
              return redisTemplate.expire(key, timeout, unit);
          }
      
          /**
           * 獲得緩存的基本對象。
           *
           * @param key 緩存鍵值
           * @return 緩存鍵值對應的數據
           */
          public <T> T getCacheObject(final String key)
          {
              ValueOperations<String, T> operation = redisTemplate.opsForValue();
              return operation.get(key);
          }
      
          /**
           * 刪除單個對象
           *
           * @param key
           */
          public boolean deleteObject(final String key)
          {
              return redisTemplate.delete(key);
          }
      
          /**
           * 刪除集合對象
           *
           * @param collection 多個對象
           * @return
           */
          public long deleteObject(final Collection collection)
          {
              return redisTemplate.delete(collection);
          }
      
          /**
           * 緩存List數據
           *
           * @param key 緩存的鍵值
           * @param dataList 待緩存的List數據
           * @return 緩存的對象
           */
          public <T> long setCacheList(final String key, final List<T> dataList)
          {
              Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
              return count == null ? 0 : count;
          }
      
          /**
           * 獲得緩存的list對象
           *
           * @param key 緩存的鍵值
           * @return 緩存鍵值對應的數據
           */
          public <T> List<T> getCacheList(final String key)
          {
              return redisTemplate.opsForList().range(key, 0, -1);
          }
      
          /**
           * 緩存Set
           *
           * @param key 緩存鍵值
           * @param dataSet 緩存的數據
           * @return 緩存數據的對象
           */
          public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
          {
              BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
              Iterator<T> it = dataSet.iterator();
              while (it.hasNext())
              {
                  setOperation.add(it.next());
              }
              return setOperation;
          }
      
          /**
           * 獲得緩存的set
           *
           * @param key
           * @return
           */
          public <T> Set<T> getCacheSet(final String key)
          {
              return redisTemplate.opsForSet().members(key);
          }
      
          /**
           * 緩存Map
           *
           * @param key
           * @param dataMap
           */
          public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
          {
              if (dataMap != null) {
                  redisTemplate.opsForHash().putAll(key, dataMap);
              }
          }
      
          /**
           * 獲得緩存的Map
           *
           * @param key
           * @return
           */
          public <T> Map<String, T> getCacheMap(final String key)
          {
              return redisTemplate.opsForHash().entries(key);
          }
      
          /**
           * 往Hash中存入數據
           *
           * @param key Redis鍵
           * @param hKey Hash鍵
           * @param value 值
           */
          public <T> void setCacheMapValue(final String key, final String hKey, final T value)
          {
              redisTemplate.opsForHash().put(key, hKey, value);
          }
      
          /**
           * 獲取Hash中的數據
           *
           * @param key Redis鍵
           * @param hKey Hash鍵
           * @return Hash中的對象
           */
          public <T> T getCacheMapValue(final String key, final String hKey)
          {
              HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
              return opsForHash.get(key, hKey);
          }
      
          /**
           * 刪除Hash中的數據
           *
           * @param key
           * @param hkey
           */
          public void delCacheMapValue(final String key, final String hkey)
          {
              HashOperations hashOperations = redisTemplate.opsForHash();
              hashOperations.delete(key, hkey);
          }
      
          /**
           * 獲取多個Hash中的數據
           *
           * @param key Redis鍵
           * @param hKeys Hash鍵集合
           * @return Hash對象集合
           */
          public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
          {
              return redisTemplate.opsForHash().multiGet(key, hKeys);
          }
      
          /**
           * 獲得緩存的基本對象列表
           *
           * @param pattern 字符串前綴
           * @return 對象列表
           */
          public Collection<String> keys(final String pattern)
          {
              return redisTemplate.keys(pattern);
          }
      }
      View Code

      WebUtils

      package com.bijian.security_demo1.utils;
      
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      
      public class WebUtils
      {
          /**
           * 將字符串渲染到客戶端
           *
           * @param response 渲染對象
           * @param string 待渲染的字符串
           * @return null
           */
          public static String renderString(HttpServletResponse response, String string) {
              try
              {
                  response.setStatus(200);
                  response.setContentType("application/json");
                  response.setCharacterEncoding("utf-8");
                  response.getWriter().print(string);
              }
              catch (IOException e)
              {
                  e.printStackTrace();
              }
              return null;
          }
      }
      View Code

      11.resources/mapper

      MenuMapper

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
      <mapper namespace="com.bijian.security_demo1.mapper.MenuMapper">
          <select id="selectPermsByUserId" resultType="java.lang.String">
              SELECT
                  DISTINCT m.`perms`
              FROM
                  sys_user_role ur
                  LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
                  LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
                  LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
              WHERE
                  user_id = #{userid}
                  AND r.`status` = 0
                  AND m.`status` = 0
          </select>
      </mapper>

      12.test

      MapperTest

       

      package com.bijian.security_demo1;
      
      import com.bijian.security_demo1.demain.User;
      import com.bijian.security_demo1.mapper.MenuMapper;
      import com.bijian.security_demo1.mapper.UserMapper;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      import org.springframework.security.crypto.password.PasswordEncoder;
      
      import java.util.List;
      
      @SpringBootTest
      public class MapperTest {
      
          @Autowired
          private UserMapper userMapper;
      
          @Autowired
          private MenuMapper menuMapper;
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          @Test
          public void testUserMapper(){
              List<User> users = userMapper.selectList(null);
              System.out.println(users);
          }
      
          @Test
          public void TestBCryptPasswordEncoder(){
              // $2a$10$VdOdYCdkS6eszRsodoLe6OY.Y/wCPzGAaC0.m8zOhhhO2lHTq9Y2a
              BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
      
              String encode = passwordEncoder.encode("123456");
      //        String encode2 = passwordEncoder.encode("123456");
              System.out.println(encode);
      //        System.out.println(encode2);
      
      //        boolean result = passwordEncoder.matches("123456", "$2a$10$VdOdYCdkS6eszRsodoLe6OY.Y/wCPzGAaC0.m8zOhhhO2lHTq9Y2a");
      //        System.out.println(result);
          }
      
          @Test
          public void testSelectPermsByUserId(){
              List<String> list = menuMapper.selectPermsByUserId(1L);
              System.out.println(list);
          }
      
      }

       

      posted on 2023-07-22 10:11  晨曦生輝耀匕尖  閱讀(40)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久精品国产99亚洲精品| 婷婷久久香蕉五月综合加勒比 | 国产精品天干天干综合网| 午夜福利国产精品视频| 国产360激情盗摄全集| 亚洲国产午夜精品福利| 成人国产欧美大片一区| 日日噜噜噜夜夜爽爽狠狠视频| 国产午夜视频在线观看| 国产精品免费无遮挡无码永久视频| 人人妻人人狠人人爽| 777米奇色狠狠888俺也去乱| 18禁黄网站免费| 精品国产乱码久久久久APP下载| 曲沃县| 风韵丰满熟妇啪啪区老熟熟女| 亚洲欧洲日韩国内精品| 天天影视色香欲综合久久| 久久男人av资源站| 猫咪www免费人成网站| 亚洲综合精品成人| 欧洲码亚洲码的区别入口| ww污污污网站在线看com| 久激情内射婷内射蜜桃| 国产在线乱子伦一区二区| 农村乱色一区二区高清视频| 久久国产精品色av免费看| 中文字幕制服国产精品| 国产偷国产偷亚洲清高| 国产成人午夜精品永久免费| 四虎精品国产精品亚洲精| 精品偷拍被偷拍在线观看 | 亚洲国产一区二区三区亚瑟| 国产嫩草精品网亚洲av| 人妻激情偷一区二区三区| 欧美精品久久天天躁| 熟妇激情一区二区三区| 亚洲天堂伊人久久a成人| 丰满少妇被猛烈进出69影院| 午夜成人理论无码电影在线播放| 库伦旗|