Rumah  >  Artikel  >  Java  >  Bagaimanakah springboot mengintegrasikan JWT untuk mencapai pengesahan identiti?

Bagaimanakah springboot mengintegrasikan JWT untuk mencapai pengesahan identiti?

王林
王林ke hadapan
2023-05-14 19:25:101477semak imbas

1. Apakah itu JWT

JSON Web Token (JWT) ialah penyelesaian pengesahan merentas domain yang paling popular. Pembangunan projek semasa secara amnya melibatkan pemisahan bahagian hadapan dan hujung hujung, yang melibatkan isu silang domain dan pengesahan.

2. Komposisi JWT

terdiri daripada tiga bahagian: Pengepala, Muatan dan tandatangan

Tajuk :

Maklumat pengepala terdiri daripada dua bahagian: 1. Jenis token, iaitu, JWT 2. Algoritma tandatangan yang digunakan, seperti HMASSHA256 atau RSA

{
  "alg": "HS256",
  "typ": "JWT"
}

Dalam json ini Atribut taip digunakan untuk mengenal pasti keseluruhan token; rentetan ialah rentetan JWT; atribut algnya digunakan untuk menerangkan algoritma tandatangan dan ringkasan yang digunakan semasa mengeluarkan JWT ini Nama penuh atribut taip dan alg sebenarnya ialah jenis algoritma. Sebab mengapa mereka diwakili oleh tiga huruf juga berdasarkan pertimbangan saiz rentetan akhir JWT Ia juga konsisten dengan nama JWT, jadi ketiga-tiga aksara itu...taip dan alg ialah atribut yang dinyatakan dalam. nama standard JWT.

Beban:

beban digunakan untuk membawa data yang akan dipindahkan. Struktur jsonnya sebenarnya adalah satu set pengisytiharan untuk data yang akan dipindahkan oleh JWT ini pernyataan dipanggil tuntutan oleh piawaian JWT Salah satu "pasangan nilai atribut" sebenarnya adalah tuntutan (keperluan) Setiap tuntutan mewakili makna dan fungsi tertentu.

Kami boleh meletakkan beberapa maklumat perniagaan dalam tuntutan.

Tandatangan (tandatangan):

Tandatangan ialah dua rentetan yang diperoleh selepas pengekodan base64url struktur json yang sepadan dengan pengepala dan muatan serta menyambungkannya bersama-sama dengan 'tempoh Inggeris ' , dan kemudian dijana mengikut algoritma tandatangan yang ditentukan oleh alg dalam pengepala.
Algoritma yang berbeza membawa kepada hasil tandatangan yang berbeza. Ambil alg: HS256 sebagai contoh untuk menggambarkan cara mendapatkan tandatangan sebelumnya.

Menurut huraian sebelumnya tentang nilai alg yang tersedia, HS256 sebenarnya mengandungi dua algoritma: algoritma HMAC dan algoritma SHA256 yang pertama digunakan untuk menjana ringkasan, dan yang terakhir digunakan untuk menandatangani secara digital hadam. Kedua-dua algoritma ini juga boleh secara kolektif dirujuk sebagai HMACSHA256

jwt rajah struktur data:

Bagaimanakah springboot mengintegrasikan JWT untuk mencapai pengesahan identiti?

3. Prinsip operasi JWT

1. Bab Sebaik sahaja permintaan log masuk dihantar, ia tidak dapat tidak akan membawa maklumat pengguna tanpa nama dan pwd

2. Jika maklumat pengguna uname dan pwd berjaya dilog masuk, maklumat pengguna akan dijana sebagai rentetan yang disulitkan

3 Rentetan yang disulitkan akan dihantar ke bahagian hadapan dalam bentuk pengepala respons

4 dibawa oleh pengepala respons dan masukkan ke dalam Vuex

5 Apabila permintaan kedua dibuat, terdapat pemintas permintaan dalam pelayan hadapan, yang akan meletakkan rentetan jwt dalam Vuex ke dalam permintaan pengepala permintaan.

6. Apabila permintaan dibuat melalui domain silang Mencapai pelayan hujung belakang, terdapat penapis lain dalam pelayan hujung belakang, yang akan memintas rentetan jwt dalam pengepala permintaan

7 Kelas alat jwt akan menghuraikan rentetan jwt, menghuraikannya ke dalam maklumat pengguna, dan akhirnya mengesahkannya

4 Springboot mengintegrasikan JWT

Idea keseluruhan:

Apabila bahagian depan-. end mengakses antara muka log masuk bahagian belakang untuk log masuk, ia mula-mula menentukan sama ada pengguna wujud dalam jadual pengguna berdasarkan nama pengguna dan kata laluan Jika ya, Untuk pengguna ini, rentetan jwt dijana dan beberapa maklumat perniagaan boleh ditambah rentetan jwt (seperti akaun log masuk, nama sebenar pengguna, dsb.), dan rentetan jwt dikembalikan ke hujung hadapan

Penghujung semasa mendapat rentetan jwt dan meletakkannya pada pengepala semua permintaan , seperti token=jwt string

Bahagian belakang membangunkan penapis untuk memintas semua permintaan (kecuali permintaan log masuk, kerana permintaan log masuk belum menghasilkan jwt), dan dapatkan jwt daripada pengepala permintaan (iaitu nilai daripada token), semak jwt checksum untuk mendapatkan maklumat perniagaan dalam jwt, dan letakkan maklumat perniagaan dalam pengepala permintaan, supaya antara muka bahagian belakang boleh mendapatkannya terus daripada pengepala

Jika dalam penapis Jika jwt tamat tempoh atau pengesahan gagal, gesaan akan dikembalikan ke bahagian hadapan, dan bahagian hadapan akan kembali ke halaman log masuk untuk membolehkan pengguna log masuk semula.

1. Perkenalkan kebergantungan dalam pom.xml

<!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.3</version>
        </dependency>

2. Bangunkan kelas alat penjanaan jwt, kodnya adalah seperti berikut:

package com.lsl.exam.utils;
 
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.lsl.exam.entity.TabUser;
 
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
public class JwtUtil {
 
    private static final long EXPIRE_TIME = 1000 * 60 * 60 *24;
 
    //设置私钥
    private static final String TOKEN_SECRET = "aa082c-66rt89-29sr3t-y9t7b8";
 
 
    /**
     * 创建携带自定义信息和声明的自定义私钥的jwt
     * @param user  用户信息表
     * @return  jwt串
     */
    public static String creatJwt(TabUser user){
        //构建头部信息
        Map<String,Object> header = new HashMap<>();
        header.put("typ","JWT");
        header.put("alg","HS256");
 
        //根据私钥构建密钥信息
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
 
        //根据当前用户密码构建密钥信息
//        Algorithm algorithm = Algorithm.HMAC256(user.getUserpwd());
 
        //设置过期时间为当前时间一天后
        Date nowDate = new Date();
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
 
        String jwt = JWT.create().withHeader(header)
                .withClaim("account",user.getAccount())//业务信息:员工号
                .withClaim("username",user.getUsername())//业务信息:员工姓名
                .withClaim("rolename",user.getRoleName())//业务信息:角色
                .withIssuer("SERVICE")//声明,签名是有谁生成 例如 服务器
                .withNotBefore(new Date())//声明,定义在什么时间之前,该jwt都是不可用的
                .withExpiresAt(expireDate)//声明, 签名过期的时间
                .sign(algorithm);//根据algorithm生成签名
 
        return jwt;
 
    }
}

3 seperti berikut:

package com.lsl.exam.controller;
 
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lsl.exam.entity.TabUser;
import com.lsl.exam.entity.backresult.ResultVO;
import com.lsl.exam.service.ITabRoleService;
import com.lsl.exam.service.IUserService;
import com.lsl.exam.utils.Base64Util;
import com.lsl.exam.utils.JwtUtil;
import com.lsl.exam.utils.ResultVoUtil;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
 
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@RestController
@RequestMapping("/exam")
public class UserController {
    private static final Logger LOG = org.slf4j.LoggerFactory.getLogger("UserController");
 
    @Autowired
    IUserService userService;
 
    @Autowired
    ITabRoleService roleService;
 
    @PostMapping(value = "login",produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVO<?> login(@RequestBody Map params){
        Map reuslt = new HashMap();
 
        String account = params.get("account") == null ? "" : params.get("account").toString();
        String pwd = params.get("pwd") == null ? "" : params.get("pwd").toString();
 
        if ("".equals(account) || "".equals(pwd)){
            return ResultVoUtil.error(30000,"用户名或者密码不能为空!");
        }
 
        //pwd解密
        String decodePwd = Base64Util.decode(pwd);
        if ("".contains(decodePwd)){
            return ResultVoUtil.error(30000,"密码错误!");
        }
 
        TabUser user = userService.getOne(new QueryWrapper<TabUser>()
                .eq("account",account)
                .eq("userpwd",decodePwd));
        if (null == user){
            return ResultVoUtil.error(30000,"用户名或者密码错误");
        }
        
        //获取当前用户拥有的角色
        String userId = user.getId();
        Map roleMap = new HashMap();
        roleMap.put("userId",userId);
        List<Map> roleList = roleService.qryRoleInfoByUserId(roleMap);
        List<String> roleNames = new ArrayList<>();
        for(Map role : roleList){
            roleNames.add(role.get("role").toString());
        }
        user.setRoleName(JSON.toJSONString(roleNames));
 
        //生成带有业务信息的jwt串
        String jwt = JwtUtil.creatJwt(user);
        
        //把jwt和当前用户信息返给前端
        reuslt.put("jwt",jwt);
        reuslt.put("roleNames",roleNames);
        reuslt.put("username",user.getUsername());
        reuslt.put("account",user.getAccount());
        
        return ResultVoUtil.success(reuslt);
    }
 
    @PostMapping(value = "qryUser",produces = "application/json;charset=UTF-8")
    @ResponseBody
    public Object qryUser(HttpServletRequest request){
 
        //这里header中的信息是filter中放进去的
        String account = request.getHeader("account");
        String username = request.getHeader("username");
        String rolename = request.getHeader("rolename");
 
        List<TabUser> list = userService.list();
        return ResultVoUtil.success(list);
    }
}

4. Bangunkan penapis dan lakukan pengesahan jwt

package com.lsl.exam.filter;
 
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.lsl.exam.entity.backresult.ResultVO;
import com.lsl.exam.utils.ResultVoUtil;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.stereotype.Component;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * jwt校验过滤器
 */
@Component
@WebFilter(filterName = "jwtFilter",urlPatterns = {"/*"})
public class AuthJwtFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String url = httpServletRequest.getRequestURL().toString();
        
        //配置不进行jwt校验的请求路径
        List<String> urlList = new ArrayList<>();
        urlList.add("/exam/login");
 
        boolean flag = false;
        for (String strUrl : urlList){
            if (url.contains(strUrl)){
                flag = true;
            }
        }
 
        try {
            if (!flag){
                String token = httpServletRequest.getHeader("token");
                //校验token,jwt过期有jwt自行校验,如果超时了,会执行catch里代码
                DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("aa082c-66rt89-29sr3t-y9t7b8")).build().verify(token);
                
                //获取jwt中的业务信息
                String account = decodeJwt.getClaim("account").asString();
                String username = decodeJwt.getClaim("username").asString();
                String rolename = decodeJwt.getClaim("rolename").asString();
                Map<String, String> headerMap = new HashMap<>();
                headerMap.put("account",account);
                headerMap.put("username",username);
                headerMap.put("rolename",rolename);
                
                //把业务信息添加到request的header
                addHeader(httpServletRequest,headerMap);
 
 
//                Class<?> superclass = servletRequest.getClass().getSuperclass().getSuperclass();
//                Field requestField = superclass.getDeclaredField("request");
//                requestField.setAccessible(true);
//                RequestFacade requestFacadeInstance = (RequestFacade) requestField.get(servletRequest);
                RequestFacade requestFacadeInstance = (RequestFacade)superclass3;
//                Field requestField1 = requestFacadeInstance.getClass().getDeclaredField("request");
//                requestField1.setAccessible(true);
//                Object requestInstance =  requestField1.get(requestFacadeInstance);
//                Field coyoteRequestField = requestInstance.getClass().getDeclaredField("coyoteRequest");
//                coyoteRequestField.setAccessible(true);
//
//                Object coyoRequestInstance =  requestField1.get(requestInstance);
//                Field headersField = coyoRequestInstance.getClass().getDeclaredField("headers");
//                headersField.setAccessible(true);
//
//                MimeHeaders headers = (MimeHeaders) headersField.get(coyoRequestInstance);
//                headers.removeHeader("token");
//                headers.addValue("account").setString(account);
//                headers.addValue("username").setString(username);
//                headers.addValue("roleid").setString(roleid);
//
 
            }
        } catch (Exception e) {
            //jwt校验失败,返给前端的code=1,前端要重定向到登录页面
            PrintWriter writer = null;
            servletResponse.setCharacterEncoding("UTF-8");
            servletResponse.setContentType("text/html; charset=utf-8");
 
            try {
                writer = servletResponse.getWriter();
                ResultVO vo = ResultVoUtil.successLogout();
                String msg = JSON.toJSONString(vo);
                writer.println(msg);
            } catch (IOException ex) {
 
            } finally {
                if (writer != null){
                    writer.close();
                }
                return;
            }
 
        }
 
        filterChain.doFilter(servletRequest,servletResponse);
    }
 
    /**
     * 向request的header中放业务信息
     * @param request
     * @param headerMap
     */
    private void addHeader(HttpServletRequest request, Map<String, String> headerMap) {
        if (headerMap==null||headerMap.isEmpty()){
            return;
        }
 
        Class<? extends HttpServletRequest> c=request.getClass();
        //System.out.println(c.getName());
        System.out.println("request实现类="+c.getName());
        try{
            Field requestField=c.getDeclaredField("request");
            requestField.setAccessible(true);
 
            Object o=requestField.get(request);
            Field coyoteRequest=o.getClass().getDeclaredField("coyoteRequest");
            coyoteRequest.setAccessible(true);
 
            Object o2=coyoteRequest.get(o);
            System.out.println("coyoteRequest实现类="+o2.getClass().getName());
            Field headers=o2.getClass().getDeclaredField("headers");
            headers.setAccessible(true);
 
            MimeHeaders mimeHeaders=(MimeHeaders) headers.get(o2);
            for (Map.Entry<String,String> entry:headerMap.entrySet()){
                mimeHeaders.removeHeader(entry.getKey());
                mimeHeaders.addValue(entry.getKey()).setString(entry.getValue());
            }
 
        }catch (Exception e){
            e.printStackTrace();
        }
 
    } 
    @Override
    public void destroy() {
 
    }
}

Atas ialah kandungan terperinci Bagaimanakah springboot mengintegrasikan JWT untuk mencapai pengesahan identiti?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam