当前位置: 首页 > news >正文

SpringSecurity+前端项目+redis完成认证授权的代码

1. 前端准备工作--都在全局main.js页面中设置的

1.1. 创建Vue工程后,并导入element ui和axios,添加连接后端项目的路径,把axios挂载到Vue

1.2. 前置路由守卫(所有路由跳转前核实一下身份)

//前置路由守卫--所有的路由跳转都先经过这里// to:即将要访问的路径  from:从哪里来 next:放行函数//解决登录之后关闭浏览器之后,还可以不用登录就能进入登录后的页面router.beforeEach((to, from, next) => {//如果是  登录页面 或者 token为真 就放行    其他情况强制跳转到登录页面const token = sessionStorage.getItem('token');if (to.path==="/login" || token){return next();}return next("/login");})

1.3. 请求过滤器(所有请求前都需携带令牌给后端)

//请求拦截器--每次请求前都会经过的
//这里的意义就是需要每次向后端发送请求都要携带者token令牌
axios.interceptors.request.use(function (config) {let token = sessionStorage.getItem('token');if (token){config.headers.token = token;}return config;
})

1.4. 响应拦截器(后端响应过来的json数据,根据code状态码判断成功/失败,并返回json数据给对应的请求,这里请求处只需要写对应的操作即可)

//设置响应拦截器
axios.interceptors.response.use(function (response) {if (response.data.code===200){Vue.prototype.$message.success(response.data.msg)return response;}else {Vue.prototype.$message.error(response.data.msg);return response;}
})

1.5. 页面布局路由导入跳转和请求

<template><div class="home"><h1>主页页面</h1><el-button type="success" @click="add()">增加</el-button><el-button type="danger" @click="del()">删除</el-button><el-button type="info" @click="update()">修改</el-button><el-button type="primary" @click="sel()">查询</el-button><el-button type="warning" @click="exp()">导出</el-button><el-button icon="el-icon-switch-button" @click="outLogin()">退出</el-button></div>
</template><script>
export default {name: 'home',data(){return{}},methods:{//退出outLogin(){this.$axios.post("/logout").then((res) => {sessionStorage.removeItem("token")this.$router.push("/login")})},//添加用户add(){this.$axios.get("/user/insert").then(res=>{})},//删除用户del(){this.$axios.get("/user/delete").then(res=>{})},//修改用户update(){this.$axios.get("/user/update").then(res=>{})},//查询sel(){this.$axios.get("/user/select").then(res=>{})},//导出exp(){this.$axios.get("/user/export").then(res=>{})}},
}
</script>
<style>
.home{margin: auto;text-align: center;
}
</style>

2. 后端准备工作

2.1. JWT工具类,客户端与服务器通信,都要带上jwt,放在http请求的头信息中

2.1.1. 引入jar
<!--引入jwt的依赖-->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version>
</dependency>
2.1.2. 创建jwt的工具类

①创建令牌---②校验令牌---③根据token获取自定义的信息

package com.wjy.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Verification;import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class JWTUtil {private static final String SECRET = "wjyGyh";//通过jwt创建token令牌public static String createToken(Map<String,Object> map){Map<String, Object> head = new HashMap<>();head.put("alg", "HS256");head.put("typ", "JWT");Date date = new Date();//当前时间--发布时间Calendar instance = Calendar.getInstance();//获取当前时间instance.set(Calendar.SECOND,7200);//当前时间的基础上添加2个小时Date time = instance.getTime();return JWT.create().withHeader(head)//头部信息.withIssuedAt(date)//发布日期.withExpiresAt(time)//过期时间.withClaim("userinfo",map)//存放用户信息--设置个人信息.sign(Algorithm.HMAC256(SECRET));//签名}//校验tokenpublic static boolean verify(String token){Verification require = JWT.require(Algorithm.HMAC256(SECRET));try {require.build().verify(token);return true;} catch (JWTVerificationException e) {System.out.println("token错误,校验失败");return false;}}//根据token获取自定义的信息public static Map<String,Object> getTokenInfo(String token,String key){return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token).getClaim(key).asMap();}
}

2.2. 加密、认证、授权、权限管理

(包含了对密码加密、用户认证授权、登录前后的权限设置==》开启允许跨域

由于都是无返回值类型的,所有使用到了fastjson)

package com.wjy.config;import com.alibaba.fastjson2.JSON;
import com.wjy.filter.LoginFilter;
import com.wjy.service.MyUserDetailService;
import com.wjy.util.JWTUtil;
import com.wjy.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Configuration
//如果springboot的版本2.7.0以上有其他的写法
public class MySercurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate StringRedisTemplate redisTemplate;//密码编码器--会自动加密@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Autowiredprivate MyUserDetailService myUserDetailService;//用户认证和授权@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(myUserDetailService);}@Autowiredprivate LoginFilter loginFilter;//登录前后的权限设置@Overrideprotected void configure(HttpSecurity http) throws Exception {//把自定义的过滤器放在UsernamePasswordAuthenticationFilter之前http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);//登录页面http.formLogin()//登录页面.loginPage("/login.html")//登录成功后跳转的页面.loginProcessingUrl("/login")//登录成功后跳转的页面--必须都是Post请求//.successForwardUrl("/success").successHandler(successHandler())//登录失败后跳转的页面//.failureForwardUrl("/error").failureHandler(failureHandler())//上面的页面请求无需认证--无需权限认证特权.permitAll();//退出登录--通过登录时候token令牌存入redis中,这里从头中拿到token,如何删除redis中的tokenhttp.logout(item->{item.logoutSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter writer = httpServletResponse.getWriter();String token = httpServletRequest.getHeader("token");//如果token验证成功//删除redis中的tokenredisTemplate.delete("login"+token);//返回json数据R r = new R(200, "退出成功", null);String jsonString = JSON.toJSONString(r);writer.println(jsonString);writer.flush();writer.close();});});//权限不足跳转的页面// http.exceptionHandling().accessDeniedPage("/error.html");http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());//如果使用的登录页面不是自己的--禁止跨越伪造请求的过滤器http.csrf().disable();//security登录允许跨域http.cors();//其他所有请求都需要认证http.authorizeRequests().anyRequest().authenticated();}//登录成功后给前端返回的json数据--并存到redis中private AuthenticationSuccessHandler successHandler() {return (httpServletRequest, httpServletResponse, authentication)->{//设置响应的编码httpServletResponse.setContentType("application/json;charset=utf-8");//获取输出对象PrintWriter writer = httpServletResponse.getWriter();//封装map数据Map<String, Object> map = new HashMap<>();//①存放用户名map.put("username", authentication.getName());//获取权限List<String> collect = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());//②存放权限码map.put("permissions", collect);//1.生成tokenString token = JWTUtil.createToken(map);//存放在redis中redisTemplate.opsForValue().set("login"+token,"");//2.放入token,返回json数据R r = new R(200, "登录成功", token);//转为json数据String jsonString = JSON.toJSONString(r);//给前端判断的依据writer.println(jsonString);//刷新流writer.flush();//关闭流writer.close();};}//登录失败后给前端返回的json数据private AuthenticationFailureHandler failureHandler() {return (httpServletRequest, httpServletResponse, e)->{//设置响应的编码--->获取输出对象httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter writer = httpServletResponse.getWriter();R r = new R(500, "登录失败", e.getMessage());//转为json数据-->给前端判断的依据-->刷新流-->关闭流String jsonString = JSON.toJSONString(r);writer.println(jsonString);writer.flush();writer.close();};}//权限不足后给前端返回的json数据private AccessDeniedHandler accessDeniedHandler() {return (httpServletRequest, httpServletResponse, e)->{//设置响应的编码--->获取输出对象httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter writer = httpServletResponse.getWriter();//创建R对象 并转换成json数据给前端 判断作为的依据-->刷新流-->关闭流R r = new R(403, "权限不足", e.getMessage());String jsonString = JSON.toJSONString(r);writer.println(jsonString);writer.flush();writer.close();};}
}

未登录--自定义顾虑器

package com.wjy.filter;import com.alibaba.fastjson.JSON;
import com.wjy.util.JWTUtil;
import com.wjy.vo.R;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
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;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Component//交于spring容器管理
public class LoginFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {response.setContentType("application/json;charset=utf-8");//获取请求路径String path = request.getRequestURI();//获取请求方式String method = request.getMethod();//判断请求路径是否为登录路径--是的话放行if ("/login".equals(path) && "POST".equals(method)){filterChain.doFilter(request,response);return;}//1.获取token令牌--从请求头中获取String token = request.getHeader("token");//2.判断token是否为空//①token为空if (StringUtils.isEmpty(token)){R r = new R(500, "未登录", null);PrintWriter writer = response.getWriter();String jsonString = JSON.toJSONString(r);writer.write(jsonString);writer.flush();writer.close();return;}//②token不为空或者redis中不存在token--验证token失败的情况(token不正确)if (!JWTUtil.verify(token) || !redisTemplate.hasKey("login"+token)){R r = new R(500, "token失效,登录失败", null);PrintWriter writer = response.getWriter();String jsonString = JSON.toJSONString(r);writer.write(jsonString);writer.flush();writer.close();return;}//③token不为空--验证token成功--获取token中的信息//获取token中的信息SecurityContext context = SecurityContextHolder.getContext();//--从JWT的工具类中获取Map<String, Object> userinfo = JWTUtil.getTokenInfo(token, "userinfo");//获取用户名Object username = userinfo.get("username");//获取权限码-->并转成需要的集合格式类型List<String> permissions = (List<String>) userinfo.get("permissions");List<SimpleGrantedAuthority> collect = permissions.stream().map(item -> new SimpleGrantedAuthority(item)).collect(Collectors.toList());//创建tokenUsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, collect);//将token放入上下文context.setAuthentication(authenticationToken);//④验证token成功--放行filterChain.doFilter(request,response);}
}

2.3. 跨域配置类

package com.wjy.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//跨域配置--允许跨域全局--自己定义的
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {//*全部,所有的意思registry.addMapping("/**")// 所有接口//是否发送Cookie.allowCredentials(true)// 是否发送 Cookie//放行哪些原始域.allowedOrigins("*")// 支持域--之后改成自己服务器的地址.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})// 支持方法.allowedHeaders("*").exposedHeaders("*");}
}

3. 业务层--登录

package com.wjy.service;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wjy.entity.User;
import com.wjy.mapper.PermissionMapper;
import com.wjy.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;@Service
public class MyUserDetailService implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate PermissionMapper permissionMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名查询用户信息--username必须唯一QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username",username);User user = userMapper.selectOne(wrapper);//判断用户是否存在if (Objects.nonNull(user)){//查询当前用户具有的权限List<SimpleGrantedAuthority> collection = permissionMapper.selectByUserId(user.getUserid())//根据用户id查询权限.stream()//转换流.map(p -> new SimpleGrantedAuthority(p.getPercode()))//把每次查询的权限码封装成SimpleGrantedAuthority.collect(Collectors.toList());//转换为集合return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getUserpwd(),collection);}return null;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 代码规范 —— QMQ 开发规范
  • 设计模式之拦截器模式
  • 前置(3):npm 和npx异同点
  • Layout 布局组件快速搭建
  • package-lock.json 要提交到git吗?
  • string详解(1)
  • 8.7笔记
  • TTL缓存用户数据
  • 基于paddlehub 未戴口罩检测算法
  • Python绘图入门:使用 Matplotlib 绘制折线图
  • MySQL 的 InnoDB 缓冲池里有什么?--InnoDB存储梳理(二)
  • 2024 年最佳 7 款 Java 分析器工具
  • Linux驱动入门实验班——基础驱动模板(附百问网视频链接)
  • MongoDB | MongoDB 终端查询
  • 详细分析Python链接Oracle的多种方式(附Demo)
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • input的行数自动增减
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • JavaScript设计模式系列一:工厂模式
  • Netty 4.1 源代码学习:线程模型
  • node 版本过低
  • PHP面试之三:MySQL数据库
  • RxJS: 简单入门
  • 工作手记之html2canvas使用概述
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 面试遇到的一些题
  • 排序算法之--选择排序
  • 无服务器化是企业 IT 架构的未来吗?
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • HanLP分词命名实体提取详解
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #《AI中文版》V3 第 1 章 概述
  • #AngularJS#$sce.trustAsResourceUrl
  • #nginx配置案例
  • ()、[]、{}、(())、[[]]命令替换
  • (Charles)如何抓取手机http的报文
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (附源码)ssm高校实验室 毕业设计 800008
  • (一)VirtualBox安装增强功能
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (转)shell中括号的特殊用法 linux if多条件判断
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .net 4.0发布后不能正常显示图片问题
  • .NET BackgroundWorker
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .Net Core 笔试1
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .Net接口调试与案例
  • @RequestParam,@RequestBody和@PathVariable 区别
  • [ Linux ] git工具的基本使用(仓库的构建,提交)
  • [202209]mysql8.0 双主集群搭建 亲测可用