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不太适用,因为需要把session复制多份。
即现在应用有很多服务,每个服务是一个端,想通过一个身份去登录所有的后端时,需要将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();
    }
}

(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);
    }
}


(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);
    }
}

数据库修改

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

修改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;


}

写具体业务API

实现三个API

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

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

在这里插入图片描述

根据token获取用户信息

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

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

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

注册API


前端

在这里插入图片描述

这节课实现了登录效果

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

退出登录时只需要在本地将token删除掉就行,服务器中没有存储token信息
删除在前端操作
下节课实现注册页面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值