• 4.2 配置Mysql与注册登录模块(中)


    学习目标

    1. jwt验证
    2. 后端的API
    3. 前端登录注册页面

    学习内容

    前端和后端会有跨域问题,不用传统的session验证,而使用JWT方式验证。

    http://localhost:3000/logout 退出springSecurity验证;
    http://localhost:3000/login


    后端

    这节课前端后端代码都会有,登陆验证注册;
    登陆的逻辑,很多url对应有很多页面,每个页面的权限不一样。
    登录一次只要不关闭浏览器就可以访问其它页面。

    每个URL都会对应一个controllert

    以前传统的session验证方式

    在这里插入图片描述

    现在很多应用前端后端可能会有跨域的问题,如有多个端,app端,web端;现在session不太适用,因此使用JWT验证方式。
    现在应用有很多服务,每个服务是一个端,想通过一个身份去登录所有的后端时,需要将sessionID复制很多分,放到多台服务器上面,比较麻烦。

    JWT验证方式

    JWT验证的优势:
    (1) 解决跨域;
    (2) 不需要在服务端存储

    比如会有多个服务器端,那么只需要一个令牌就可以访问多个服务器。

    原理: 原来seesion的方式,一个用户登陆成功之后,需要赋给他一个sessionID,现在给他一个JWT的Token. 服务器给他一个token之后 ,自己是不需要存储任何信息的。
    将userID或者其它信息加入到JWT token里面,

    简化版逻辑:
    在这里插入图片描述(1)密钥是存储在服务器的,对用户不公开,是一个随机字符串,自己定义的。
    (2)hash函数或者加密函数,加密成一个字符串,不可逆
    (3)JwtToken = userID+ 加密(hash)后的信息 返回给用户,完全存储到客户端;

    未来服务器验证的时候,将接收到的信息,将第一段加上密钥进行加密,看加密后的结果是否与以前加密的信息一致

    这节课目的是把session 修改为JWT

    两个疑惑:
    (1) 篡改数据,冒充UserID去访问,不可行,hash函数加密结果会变;
    (2)JwtToken是存储在用户端本地的,如果被窃取,那就是自己的问题,活该。

    token一般是存储在用户本地的,浏览器的localstory里面;

    优化方法:一般给用户传两个token,一个是access-token(有效时间比较短,如5分钟) ;另一个是refresh-token(有效时间比较长14天);
    当每次向服务器发送请求,
    带access-token,使用有效时间比较短的令牌;
    当短令牌有效期过去之后,再使用Post请求重新根据refresh-koen再获取一个新的token。
    在这里插入图片描述

    Get请求:明文不安全
    Post请求: 安全


    这节课的逻辑,登陆页面登录之后会获得一个JWT-Token;用户得到之后token存到浏览器,之后每次访问服务的时候,就带上这个token(令牌)。
    在这里插入图片描述


    JWT工具类

    实现utils.JwtUtil类,为jwt工具类,用来创建、解析jwt token
    作用
    (1)是将一个字符串加上密钥,加上有效期变成一个加密后的字符串;
    (2)另外一个作用将一个令牌将userID解析出来

    (1)加三个依赖到pom文件

    • jjwt-api
    • jjwt-impl
    • jjwt-jackson

    (2)复制工具类JwtUtil类

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.stereotype.Component;
    
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    import java.util.Base64;
    import java.util.Date;
    import java.util.UUID;
    
    @Component
    public class JwtUtil {
        public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14;  // 有效期14天
        public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";
    
        public static String getUUID() {
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
    
        public static String createJWT(String subject) {
            JwtBuilder builder = getJwtBuilder(subject, null, 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)
                    .setSubject(subject)
                    .setIssuer("sg")
                    .setIssuedAt(now)
                    .signWith(signatureAlgorithm, secretKey)
                    .setExpiration(expDate);
        }
    
        public static SecretKey generalKey() {
            byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
            return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
        }
    
        public static Claims parseJWT(String jwt) throws Exception {
            SecretKey secretKey = generalKey();
            return Jwts.parserBuilder()
                    .setSigningKey(secretKey)
                    .build()
                    .parseClaimsJws(jwt)
                    .getBody();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    (3)创建Filter工具类

    实现config.filter.JwtAuthenticationTokenFilter类,用来验证jwt token,如果验证成功,则将User信息注入上下文中

    import com.kob.backend.mapper.UserMapper;
    import com.kob.backend.pojo.User;
    import com.kob.backend.service.impl.utils.UserDetailsImpl;
    import com.kob.backend.utils.JwtUtil;
    import io.jsonwebtoken.Claims;
    import org.jetbrains.annotations.NotNull;
    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;
    
    @Component
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
        @Autowired
        private UserMapper userMapper;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
            String token = request.getHeader("Authorization");
    
            if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
                filterChain.doFilter(request, response);
                return;
            }
    
            token = token.substring(7);
    
            String userid;
            try {
                Claims claims = JwtUtil.parseJWT(token);
                userid = claims.getSubject();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    
            User user = userMapper.selectById(Integer.parseInt(userid));
    
            if (user == null) {
                throw new RuntimeException("用户名未登录");
            }
    
            UserDetailsImpl loginUser = new UserDetailsImpl(user);
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginUser, null, null);
    
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    
            filterChain.doFilter(request, response);
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    (4) 配置config.SecurityConfig类,放行登录、注册等接口

    import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    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.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    
        @Bean
        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().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/user/account/token/", "/user/account/info/").permitAll()
                    .antMatchers(HttpMethod.OPTIONS).permitAll()
                    .anyRequest().authenticated();
    
            http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    数据库修改

    修改表
    在这里插入图片描述
    创建一个列,存储头像
    存储的都是头像链接;以后图片可以存储到图床.在这里插入图片描述
    在这里插入图片描述

    修改pojo,因为实体是和数据库相匹配的。

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        // 在mabatis-plus中 最好不要使用int ,而去使用对象Integer
        @TableId(type = IdType.AUTO)  // 目的是让id自增(mybatis-plus里面的)
        private Integer id;
        private String username;
        private String password;
        private String photo;
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    写具体业务API

    实现三个API

    在这里插入图片描述写api的流程,三个地方
    (1) 写service里面写接口,
    InfoService,LoginService, RegisterService
    (2) 在service / impl写接口的实现
    (3) 写controller里面,调用service

    RegisterService接口和 LoginService接口是公开的,没有帐号之前不能锁住
    登陆成功之后就可以获取用户的信息了。

    在这里插入图片描述

    根据token获取用户信息

    这里使用第一个用户的token就获取第一个用户的信息
    在这里插入图片描述在这里插入图片描述

    试验: 使用第二个用户登陆验证,要更换对应的token
    token =用户id+ 加密信息(用户id+随机密钥)

    结果:使用谁的token就获取谁的信息

    注册API


    前端

    在这里插入图片描述

    这节课实现了登录效果

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    下节课实现注册页面

  • 相关阅读:
    STM32 NVIC中断优先级管理通过结构图快速理解
    从双非硕士到大厂工作,优秀
    Spring Boot的配置文件
    组合式API_模板引用
    php判断文件类型是否是图片
    WuThreat身份安全云-TVD每日漏洞情报-2023-10-07
    大数据运维实战第二十三课 Namenode、Datanode、Nodemanager 等服务状态监控策略
    260. 只出现一次的数字 III
    Python编程 列表的常用方法
    快速排序的按区间的三个版本及优化--友友们不一定了解
  • 原文地址:https://blog.csdn.net/qq_36288669/article/details/126007829