使用jwt进行登录

 

一、遇见的错误汇总

1.数据库

(1)

解决:

net stop mysql 关闭数据库

mysqld --console --skip-grant-tables --shared-memory     重新打开一个窗口,设置免密登录数据库

重新 打开一个窗口输入,这个时候可以不用输入密码 mysql -u root    

设置密码为空update user set authentication_string='' where user='root';

(2)

解决:未启动数据库  net start mysql   

(3)启动springboot后数据库报错

解决:配置文件里应该是ip地址而不是连接名称

2.springboot

(1)

解决:springboot和mybatis版本不兼容 ,修改后的配置文件如下

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!--    jwt-->

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </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>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>


    </dependencies>

(2)空指针异常/没反应/找不到

解决:记得加上注解

 (3)apifox里的登录接口报错403

解决:创建接口时记得把请求方法的get改成post

(4)请求加密解密报错

解决:更换jjwt的依赖,不同的依赖加密解密的方法可能不同

(5)连接失败

解决:经过查看发现是没有配置redis的数据连接

二、jwt

1.概念及流程

 

2.要做的事情

  • 登录 。要做(1)自定义登录接口 实现到provider的查询;成功则生成jwt,并存入redis(2)实现userdetailservice,实现到数据库的自定义查询

  • 校验:定义jwt认证过滤器,获取token,解析token,用userid从redis中获取信息,存入contextholder中(让其他过滤器可以访问 )

  • 关联:将userid存到redis,当用户发起请求时使用userid去redis里查询

 

 

3.代码编写

  • 准备工作

(1)引入依赖 ,依赖见第一部分的springboot报错处

(2)添加redis的序列化器和配置类、以及其他工具类,代码见下面链接,直接复制就可以。添加的工具包如下

FastJson序列化、redis配置类、FastJsonRedisSerializer、RedisConfig、RedisCache、JwtUtils工具类、跨域配置、MP分页拦截器-CSDN博客

最后添加完的目录如下

(3)在application.yml文件里配置数据库的连接。创建user数据库,并在代码中创建user实体类,上面记得加上@tablename注解

spring:


  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mydemo?useUnicode=true&
      characterEncoding=UTF-8&
      useJDBCCompliantTimezoneShift=true&
      useLegacyDatetimeCode=false&
      serverTimezone=Asia/Shanghai&
      allowMultiQueries=true&
      useAffectedRows=true
    username: root
    password:

(4)创建mapper包,并在application上面加上包扫描

 

  • 实现数据库校验核心用户

(1)编写userdetailservice的实现类,重写方法:userdetailservice接口会接受用户登陆时填写的用户名和密码,我们只要继承这个接口实现对数据库的查询。原本的代码里userdetailservice会查询后封装成detail返回,现在我们继承service后要返回继承了detail的类。

(2)查询到有结果的时就封装成userdetails返回(创建loginuser来实现),没结果就抛出异常(异常处理已经在前面的类里实现)

(3)loginuser记得把下面默认的false改成true。注意,需要往用户表中写入用户数据,并且如果想让用户的密码是明文存储,需要在密码前加{noop}。例如:{noop}1234

(4)将密码改成密文传输:默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password ,它会根据id去判断密码的加密方式。

         我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码验。

        方式:定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承WebSecurityConfigurerAdapter。这里使用@EnableWebSecurity注解开启Spring Security的功能 代替了 implements WebSecurityConfigurerAdapter

        ps:PasswordEncoder是BCryptPasswordEncoder的父类

  • 实现登录接口 :生成jwt+存入redis

思路:自定义一个登录接口,在不需要登陆的时候也可以访问。

=>通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。原本用authenticate认证是使用authentiction类型认证,因此这里的user也应该封装成authentiction,这里使用
 UsernamePasswordAuthenticationToken封装。AuthenticationManager最后会调用前面编写的userdetailservice去数据库进行认证。

=>认证成功的话要生成一个jwt,放入响应中返回;并且为了让用户下回请求时能通过jwt识别出具体的是哪个用户,我们需要把用户信息存入redis,可以把用户id作为key

(1)SecurityConfig配置放行接口

(2)在impl类里面请求。此时认证返回的authenticate的principal封装了之前自己写的loginuser,因此要通过get获取;最后将user存入redis并返回给前端token

 

  • 实现过滤器|| 获取token=》解析token=》从redis中获取用户信息=》存入securitycontexthoder(后面的请求都是从此地方获取信息)

(1)使用OncePerRequestFilter作为过滤器,因为使用默认的过滤器可能会被调用多次,此过滤器只会调用一次。能查得到则放行

(2)配置过滤器到前面,以便接受请求,在securityconfig类里面配置

(3)编写退出登录接口:获取SecurityContextHolder中的认证信息,删除redis中对应的数据,并在impl和service里做相应修改

(4)自定义失败处理。返回json,这样可以让前端能对响应进行统一的处理。

在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。

如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。

如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。

所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置SpringSecurity即可。

 

posted @ 2024-11-29 15:36  超级无敌大帅比  阅读(65)  评论(0)    收藏  举报