资讯详情

SpringSecurity前后端分离

SpringSecurity前后端分离(动态鉴定)

一、解释认证流程

1.原始认证流程

原始认证过程通常与Session一起使用,但是前后端分离后能使用Session了

SpringSecurity默认认证程序如下图所示(此图为B站UP主三更草堂讲SpringSecurity课程的图)

图片描述

DaoAuthenticationProvider继承AbstractUserDetailsAuthenticationProvider抽象类,而AbstractUserDetailsAuthenticationProvider抽象类又实现了AuthenticationProvider这个接口。

AuthenticationProvider接口和AuthenticationManager接口都有 authenticate() 这个方法

认证流程:

1.输入用户名和密码

2、UsernamePasswordAuthenticationFilter将用户名和密码包装成密码Authentication对象

然后再调用AuthenticationManager接口中的authenticate()方法进行认证,在AuthenticationManager接口实现类ProviderManager重写也被调用authenticate()认证方法。抽象类。AbstractUserDetailsAuthenticationProvider中重写了authenticate()方法

4、AbstractUserDetailsAuthenticationProviderauthenticate()该方法调用抽象法retrieveUser()方法

5、DaoAuthenticationProvider在重写方法retrieveUser()里调用了loadUserByUsername()方法

6、loadUserByUsername()方法会返回UserDetails对象认证成功返回上一层

2.前后端分离认证过程

在前后端分离后,我们要求在认证成功或失败时返回相应的状态代码,然后我们不再使用它Session经常使用认证管理jwt(JSON Web Token)认证引出了两种前后分离的写法

(图为B站UP主三更草堂讲SpringSecurity课程的图)

无论使用以下哪种写法,这里都需要UsernamePasswordAuthenticationFilter在前面加一个过滤器Token认证,如果Token认证成功意味着用户已登录;Token认证失败表明未登录或登录已过期。

2.1、继承UsernamePasswordAuthenticationFilter的写法

认证流程:

1.输入用户名和密码

2、MyUsernamePasswordAuthenticationFilter将用户名和密码包装成密码Authentication对象

然后再调用AuthenticationManager接口中的authenticate()认证方法,在AuthenticationManager接口实现类ProviderManager重写也被调用authenticate()认证方法。抽象类。AbstractUserDetailsAuthenticationProvider中重写了authenticate()方法

4、AbstractUserDetailsAuthenticationProviderauthenticate()该方法调用抽象法retrieveUser()方法

5、DaoAuthenticationProvider在重写方法retrieveUser()里调用了loadUserByUsername()方法,自定义AuthUserDetailsServiceImpl类实现UserDetailsService接口,重写loadUserByUsername()方法

6、在loadUserByUsername()在方法中,用户和角色将被查询并返回UserDetails对象

7、在继承WebSecurityConfigurerAdapter登录成功、失败处理器设置在类中,处理器定义返回状态码等信息

2.2.自定义写作

UsernamePasswordAuthenticationToken继承了AbstractAuthenticationToken抽象类,AbstractAuthenticationToken实现了抽象类Authentication接口

认证流程:

1.前端通过将用户名和密码发送到后端控制器,控制器调用业务层

2、Service层创建UsernamePasswordAuthenticationToken对象将用户名和密码包装成Authentication对象

3、然后调用AuthenticationManagerauthenticate()认证方法,抽象AbstractUserDetailsAuthenticationProvider中重写了authenticate()方法

4、AbstractUserDetailsAuthenticationProviderauthenticate()该方法调用抽象法retrieveUser()方法

5、DaoAuthenticationProvider在重写方法retrieveUser()里调用了loadUserByUsername()方法,自定义AuthUserDetailsServiceImpl类实现UserDetailsService接口,重写loadUserByUsername()方法

6、在loadUserByUsername()在方法中,用户和角色将被查询并返回UserDetails对象

2.3、区别

1、使用UsernamePasswordAuthenticationFilter自定义写法不需要使用登录成功和失败处理器,自定义写法可以自定义失败处理器(包括认证异常和授权异常)。登陆失败和没有权限)

2、使用UsernamePasswordAuthenticationFilter的写法对于扩展写法没那么友好,比如说添加手机验证码

二、数据库的设计

该示例是上面自定义的前后端分离的写法

这里使用的是Oracle数据库,这里没有权限的表,但是使用角色来判断也差不多

1、用户表

2、用户角色关系表

3、角色表

4、图片表

5、点赞表

三、初始配置

SpringBoot 版本是 2.6.0

1、项目结构

2、导入依赖

<dependencies>
    <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>
    <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>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--MyBatis-Plus的依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </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.74</version>
    </dependency>

    <!--hutool工具类-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.5.6</version>
    </dependency>
    <!-- mybatis-plus-generator -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.4.1</version>
    </dependency>
    <!-- lang3 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>
    <!--添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)-->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.2</version>
    </dependency>
    <!--swagger的依赖-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.7.0</version>
    </dependency>

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.7.0</version>
    </dependency>

    <!-- JWT的依赖 -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>

    <!-- mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Oracle数据库 -->
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc8</artifactId>
        <scope>runtime</scope>
    </dependency>

</dependencies>

3、代码生成器

代码生成器这里最开始使用的是mysql 8.X版本的,读者需要自己修改一下数据库的名字,如果是mysql 5.X还需要修改一下驱动

后面才改用Oracle数据库,这里的代码就懒得改了

package com.guet.APPshareimage;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;

/** * @Author LZDWTL * @Date 2021-12-15 17:09 * @ClassName CodeGenerator * @Description 代码生成器 */
public class CodeGenerator { 
        

    /** * <p> * 读取控制台内容 * </p> */
    public static String scanner(String tip) { 
        
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) { 
        
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) { 
        
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) { 
        
        // 创建代码生成器对象
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(scanner("请输入你的项目路径") + "/src/main/java");

        //作者
        gc.setAuthor("LZDWTL");
        //生成之后是否打开资源管理器
        gc.setOpen(false);
        //重新生成时是否覆盖文件
        gc.setFileOverride(false);
        //%s 为占位符
        //mp生成service层代码,默认接口名称第一个字母是有I
        gc.setServiceName("%sService");
        //设置主键生成策略 自动增长
        gc.setIdType(IdType.AUTO);
        //设置Date的类型 只使用 java.util.date 代替
        gc.setDateType(DateType.ONLY_DATE);
        //开启实体属性 Swagger2 注解
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/shareimage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("shareimage");
        dsc.setPassword("888888");
        //使用mysql数据库
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //pc.setModuleName(scanner("请输入模块名"));
        pc.setParent("com.guet.APPshareimage");
        pc.setController("controller");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setMapper("mapper");
        pc.setEntity("entity");
        pc.setXml("mapper");
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //设置哪些表需要自动生成
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));

        //实体类名称驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);

        //列名名称驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //使用简化getter和setter
        strategy.setEntityLombokModel(true);
        //设置controller的api风格 使用RestController
        strategy.setRestControllerStyle(true);
        //驼峰转连字符
        strategy.setControllerMappingHyphenStyle(true);
        //忽略表中生成实体类的前缀
        //strategy.setTablePrefix("t_");
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

运行代码生成器,复制路径输入,然后依次输入数据库中表的名字

D:\WorkSpace\JavaWorkSpce\ideal\APP-shareimage\APP-shareimage

t_user,t_picture,t_like,t_user_role,t_role

4、配置application.yml

根据自己的数据库和redis进行配置

server:
  port: 8080

spring:
  # 数据库配置
  datasource:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@120.77.80.135:1521:orcl
    username: XXXXXX
    password: XXXXXX
    # 连接池
    hikari:
      # 连接池名
      pool-name: DateHikariCP
      # 最小空闲连接数
      minimum-idle: 5
      # 空闲连接最大存活时间,默认600000(10分钟)
      idle-timeout: 180000
      # 最大连接数,默认10
      maximum-pool-size: 10
      # 从连接池返回的连接自动提交
      auto-commit: true
      # 连接最大存活时间,1800000(30分钟)
      max-lifetime: 1800000
      # 连接超时时间,默认30000(30秒)
      connection-timeout: 30000
      # 测试连接是否可用的查询语句
      #connection-test-query: SELECT 1 #这个是mysql的测试语句
      connection-test-query: SELECT * from dual  #这个是oracle的测试语句

  #redis配置
  redis:
    #服务器地址
    host: 120.77.80.135
    #端口
    port: 6379
    #redis密码
    password: XXXXXX
    #数据库,默认是0
    database: 0
    #超时时间
    timeout: 1209600000ms
    lettuce:
      pool:
        #最大链接数,默认8
        max-active: 8
        #最大连接阻塞等待时间,默认-1
        max-wait: 10000ms
        #最大空闲连接,默认8
        max-idle: 200
        #最小空闲连接,默认0
        min-idle: 5

mybatis-plus:
  mapper-locations: classpath:/mapper/*Mapper.xml
  type-aliases-package: com.guet.APPshareimage.entity

logging:
  level:
    com.guet.shareimage.mapper: debug

jwt:
  # JWT存储的请求头
  tokenHeader: Authorization
  # JWT 加解密使用的密钥
  secret: lzdwtl
  # JWT的超期限时间(1000*60*60*24*14)14天,即两周
  expiration: 1209600000
  # JWT 负载中拿到开头
  tokenHead: Bearer


role:
  roleid: 1

5、其他配置、工具类

5.1、SpringSecurity配置类

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

    @Autowired
    private MyOncePerRequestFilter myOncePerRequestFilter;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception { 
        

        //1、关闭csrf,关闭Session
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //2、设置不需要认证的URL
        http
                .authorizeRequests()
                //允许未登录的用户进行访问
                .antMatchers("/doLogin").anonymous()
                //其余url都要认证才能访问
                .anyRequest().authenticated();
    }
}

5.2、JSON格式返回配置类

public abstract class JSONAuthentication { 
        
    /** * 输出JSON * * @param request * @param response * @param obj * @throws IOException * @throws ServletException */
    protected void WriteJSON(HttpServletRequest request,
                             HttpServletResponse response,
                             Object obj) throws IOException, ServletException { 
        
        //这里很重要,否则页面获取不到正常的JSON数据集
        response.setContentType("application/json;charset=UTF-8");

        //跨域设置
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Method", "POST,GET");
        //输出JSON
        PrintWriter out = response.getWriter();
        out.write(JSON 

标签: 装饰连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台