自己开发完整项目一、登录功能-04(集成jwt)

ops/2024/9/22 22:47:29/

一、说明

前面文章我们写到了通过数据库查询出用户信息并返回,那么在真实的项目中呢,后端是需要给前端返回一个tocken,当前端通过登录功能认证成功之后,我们后端需要将用户信息和权限整合成一个tocken返回给前端,当前端再次访问别的接口的时候,需要携带着tocken,请求到达后端的时候,后端需要从前端传递过来的tocken中解析出用户信息和权限,判断是否可以访问。


实现思路:在登录中的查询用户中,将权限也查询出来,随后通过jwt来生成tocken和解析tocken

二、 编写jwt工具类(生成tocken和解析tocken)

java">package com.ljy.myspringbootlogin.utils;import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;@Component
public class JwtUtils {/*** 创建一个密钥*/private String secret = "Ljy991008X123435asdfSFS34wfsdfsdfSDSD32dfsddDDerQSNCK34SOWEK5354fdgdf4";SecretKey  key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));/*** 生成tocken*/public String createTocken(Map<String,Object> map){String tocken = Jwts.builder().setClaims(map).issuedAt(new Date()).expiration(new Date(System.currentTimeMillis() * 30 * 60 * 1000)).signWith(key).compact();return tocken;}/*** 解析tocken*/public Claims parasTocken(String tocken){Jws<Claims> claimsJws = Jwts.parser().verifyWith(key).build().parseSignedClaims(tocken);return claimsJws.getBody();}
}

三、通过jwt工具类将用户信息和权限生成tocken返回给前端

修改userServiceImpl中的代码

java">package com.ljy.myspringbootlogin.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljy.myspringbootlogin.commont.Reuslt;
import com.ljy.myspringbootlogin.mapper.UserMapper;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.service.IUserService;
import com.ljy.myspringbootlogin.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.*;@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserModel> implements IUserService {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtUtils jwtUtils;@Overridepublic Reuslt<UserModel> login(String username, String password) {System.out.println("进入serviceimpl");System.out.println(username);System.out.println(password);//传入用户名和密码UsernamePasswordAuthenticationToken tocken = new UsernamePasswordAuthenticationToken(username, password);System.out.println("tocken"+tocken);//实现登录,此时就会调用loadUserByName//authenticate其实就是userdetailsAuthentication authenticate = null;try{authenticate= authenticationManager.authenticate(tocken);System.out.println("測試!!!!!");}catch (BadCredentialsException e){return Reuslt.error(500,"用户名或者密码错误");}UserModel principal = (UserModel)authenticate.getPrincipal();System.out.println("principal:"+principal);//生成tocken   修改这里就可以HashMap<String,Object> map = new HashMap<>();map.put("id",principal.getId());map.put("username",principal.getUsername());map.put("menuList",principal.getMenuList());map.put("roleList",principal.getRoleList());String tocken1 = jwtUtils.createTocken(map);System.out.println("tocken1"+tocken1);principal.setTocken(tocken1);return Reuslt.ok(principal);}
}

四、编写自定义过滤器(作用:当前端携带tocken访问别的接口的时候,需要进行判断是否有权限访问)

java">package com.ljy.myspringbootlogin.filter;import com.ljy.myspringbootlogin.model.MenuModel;
import com.ljy.myspringbootlogin.model.RoleModel;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;/*** 这个过滤器的作用:* 当某一个用户登录认证成功之后,后端给前端返回一个tocken信息,这个tocken信息里面包含着用户的属性和用户的角色、权限信息* 那么当这个用户再次访问别的接口的时候,后端就需要对前端传递过来的tocken信息进行解析,判断里面是否有权限访问接口** 我们需要集成OncePerRequestFilter,这个是一个抽象类,其中有一个doFilter方法,在doFilter方法中调用了doFilterInternal方法,也是一个抽象方法,所以需要实现* 并且只会在请求之前执行一次** 实现逻辑:* 1.获取前端传递过来的tocken* 2.解析tocken* 3.将解析出来的用户信息和权限使用Authentication告诉给springSecurity框架,springsecurity会将信息存储到SecurityContet中,* 从而放在SecurityContetHolder中* 4.有权限就访问,没有权限就报错*** 注意:登录的时候,只需要放用户名和密码*      登录成功之后请求别的接口的时候,需要放的是用户信息和用户权限**/@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@AutowiredJwtUtils jwtUtils;/*** 这个方法会被doFilter调用* @param request* @param response* @param filterChain* @throws ServletException* @throws IOException*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {/*** 1.获取tocken* 因为前端将tocken信息放到了请求头中,所以我们需要使用request.getHeader("XXXX")来获取tocken*/String tocken = request.getHeader("Authorization");if(tocken == null){doFilter(request,response,filterChain);return;}/*** 2.解析tocken* 使用我们自己编写的工具类,JwtUtils来解析*/Claims claims = jwtUtils.parasTocken(tocken);System.out.println("解析出来的用户信息:"+claims);/*** 3.将解析出来的用户信息和权限使用Authentication告诉给springSecurity框架,springsecurity会将信息存储到SecurityContet中,* 从而放在SecurityContetHolder中*///3.1 从tocken中拿到信息Long id = claims.get("id", Long.class);String username = claims.get("username", String.class);List<String> menuList = claims.get("menuList", ArrayList.class);System.out.println("menuList:"+menuList);List<String> roleList = claims.get("roleList", ArrayList.class);//3.2 将信息放到userModel中UserModel userModel = new UserModel();userModel.setId(id);userModel.setUsername(username);userModel.setMenuList(menuList);//将权限信息转换成Collection<GrantedAuthority> grantedAuthorities = AuthorityUtils.createAuthorityList(menuList.toArray(new String[0]));userModel.setAuthorities(grantedAuthorities);System.out.println("userModel:"+userModel);//3.3 将信息当道SecurityContet中UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(userModel, "", userModel.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);System.out.println("userModel.getAuthorities():"+userModel.getAuthorities());//3.4 放行doFilter(request,response,filterChain);}
}

五、将自定义过滤器加载到springsecurity框架的过滤器链中
修改springsecurityConfig

java">package com.ljy.myspringbootlogin.config;import com.ljy.myspringbootlogin.filter.JwtAuthenticationFilter;
import com.ljy.myspringbootlogin.springSecurityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@EnableWebSecurity //开启springSecurity,会注册大量的过滤器链
@Configuration
@EnableMethodSecurity //开启方法级别的安全校验
public class springSecurityConfig {@Autowiredprivate springSecurityService springSecurityService;@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;/*** 配置过滤器链* @param http* @return* @throws Exception*/@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{http.csrf().disable();//跨域漏洞防御:关闭http.cors().disable();//跨域拦截关闭http.authorizeHttpRequests().antMatchers("/user/**").permitAll().anyRequest().authenticated();//将自己定义的过滤器添加到过滤器链中//将自定义过滤器放到认证过滤器之前http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}/*** AuthenticationManager:负责认证,也就是认证规则* DaoAuthenticationProvider:负责将springSecurityService和passwordEncoder放进AuthenticationManager中* @return*/@Beanpublic AuthenticationManager authenticationManager(){System.out.println("進入authenticationManager");DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();daoAuthenticationProvider.setUserDetailsService(springSecurityService);//关联使用的密码编码器daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());//将daoAuthenticationProvider放到ProviderManager中ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);System.out.println("結束authenticationManager");return providerManager;}/*** 密码编码器* @return*/@Beanpublic PasswordEncoder passwordEncoder(){return NoOpPasswordEncoder.getInstance();}
}

六、编写一个测试接口进行验证

java">package com.ljy.myspringbootlogin.controller;import com.ljy.myspringbootlogin.commont.Reuslt;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {//创建登录控制器@RequestMapping("/test01")@PreAuthorize("hasAnyAuthority('qxgl')")  //需要的权限public Reuslt<String> aaa(){String a="测试";return Reuslt.ok(a);}}

七、postman测试

1.登录生成tocken

2.访问测试接口,并写到上面的tocken

 

八、引入jwt依赖

java"><!--        引入jwt--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.12.6</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.12.6</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <version>0.12.6</version><scope>runtime</scope></dependency>

 九、总结

到目前位置,我们实现了前端登录成功之后,通过数据库查询用户信息,并将用户信息生成tocken返回给前端,当前端携带tocken访问权限控制接口的时候,会判断是否有权限,进而判断是否可以访问。
但是存在一个问题,在上面的代码中,我们的接口权限是通过注解的方式写死的,在真实项目中,这个是绝对不可以的,所以我们需要动态实现权限,我们在下一章进行编写!


http://www.ppmy.cn/ops/104093.html

相关文章

Promise内幕揭秘:手写实现背后的奥秘

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 在之前的文章中我们已经了解了Promise的基本用法&#xff0c;今天我们就来手写实现…

机器学习数学公式推导之高斯分布

文章目录 1、介绍引入1.1 频率派的观点1.2 贝叶斯派的观点1.3 小结 2、数学基础2.1 二阶中心矩2.2 样本方差2.3 高斯分布2.3.1 一维情况 MLE2.3.2 多维情况 本文参考 B站UP: shuhuai008 跳转 &#x1f339;&#x1f339; 1、介绍引入 在统计学和概率论中&#xff0c; P ( x ∣ …

ElementPlus实现页面,上部分是表单,下部分是表格

效果 <template><el-dialog v-model"produceDialogFormVisible" draggable custom-class"dialog-title" :title"title" :close-on-click-modal"false"><el-form label-width"120px"><el-row :gutter&q…

笔记 12 : 彭老师课本第 6 章, PWM ,代码实践

&#xff08;85&#xff09; 驱动写多了&#xff0c;会发现&#xff0c;首先就是硬件的初始化&#xff0c;配置硬件。 &#xff08;86&#xff09;查看源代码组织&#xff1a; &#xff08;87&#xff09; 编译过程不变&#xff1a; &#xff08;88&#xff09; 运行同以前的步…

Datawhale X 李宏毅苹果书 AI夏令营(深度学习进阶)task3

批量归一化 其实归一化简单一点理解就类似于我们学过的数学中的每个数值减去平均值除以标准差。 神经网络中的批量归一化&#xff08;Batch Normalization&#xff0c;BN&#xff09;就是其中一个“把山铲平”的想法。不要小看优化这个问题&#xff0c;有时候就算误差表面是凸…

大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Local GAP - Financial Statement Version 【海外BS\PL报表】

业务场景&#xff1a; 基于海外IFRS等会计准则为客户定义一套BS\PL报表 BS - 从科目余额抓取 PL - 从利润中心报表抓取 会计报表版本的建立&#xff1a; 路径&#xff1a;IMG>财务会计&#xff08;新&#xff09;>总账会计核算&#xff08;新&#xff09;主数据>总…

JUC-指令有序性

指令重排 JVM 会在不影响正确性的前提下&#xff0c;可以调整语句的执行顺序&#xff0c;思考下面一段代码 static int i; static int j; // 在某个线程内执行如下赋值操作 i ...; j ...; 可以看到&#xff0c;至于是先执行 i 还是 先执行 j &#xff0c;对最终的结果不…