SpringbootSecurity登陆验证(前后端分离)

一、什么是jwt

JWT全称是JSON Web Token,如果从字面上理解感觉是基于JSON格式用于网络传输的令牌。实际上,JWT是一种紧凑的Claims声明格式,旨在用于空间受限的环境进行传输,常见的场景如HTTP授权请求头参数和URI查询参数。JWT会把Claims转换成JSON格式,而这个JSON内容将会应用为JWS结构的有效载荷或者应用为JWE结构的(加密处理后的)原始字符串,通过消息认证码(Message Authentication Code或者简称MAC)和/或者加密操作对Claims进行数字签名或者完整性保护。

二、创建后端项目

2.1 添加pom依赖

        
        
            cn.hutool
            hutool-all
            5.8.16
        

2.2 编辑entity、mapper、service、controller

这里就不过多写了,依旧是跟上一边的文章一样

SpringbootSecurity登陆验证(前后端分离)_第1张图片

2.3 JWT 过滤器类

package com.aaa.filter;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
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.stream.Collectors;

@Component
public class JWTFilter extends OncePerRequestFilter {


    /**
     * 重写的doFilterInternal方法
     * 解析token并验证用户信息
     * 如果验证成功,则保存用户信息并放行
     * 如果验证失败,则返回错误信息
     * 如果token为空且请求路径不在白名单中,则返回错误信息
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 白名单路径
        String[] whitename = {"/login"};
        // 获取请求头中的token
        String token = request.getHeader("token");
        // 如果token不为空
        if (StringUtils.isNotBlank(token)) {
            // 验证token是否有效
            boolean verify = JWTUtil.verify(token, "user".getBytes());
            // 如果验证通过
            if (verify) {
                // 解析token,获取用户名和资源信息
                JWT jwt = JWTUtil.parseToken(token);
                String username = (String) jwt.getPayload("username");
                List resources = (List) jwt.getPayload("resources");

                // 将资源信息转换为SimpleGrantedAuthority列表
                List resourceList = resources.stream().map(res -> new SimpleGrantedAuthority(res)).collect(Collectors.toList());

                // 保存用户信息
                UsernamePasswordAuthenticationToken userPwdToken = new UsernamePasswordAuthenticationToken(username, null, resourceList);
                SecurityContextHolder.getContext().setAuthentication(userPwdToken);

                // 放行
                filterChain.doFilter(request, response);
            } else {
                // 验证失败,返回错误信息
                ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
                printJsonData(response, responseMsg);
            }
        } else {
            // 如果token为空,检查请求路径是否在白名单中
            String requestURI = request.getRequestURI();

            // 如果在白名单中,放行
            if (ArrayUtils.contains(whitename, requestURI)) {
                filterChain.doFilter(request, response);
            } else {
                // 如果不在白名单中,返回错误信息
                ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
                printJsonData(response, responseMsg);
            }
        }
    }

    public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {
        try {
            response.setContentType("application/json;charset=utf8");  // 设置响应内容类型为JSON,并指定编码为UTF-8
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(responseMsg);  // 使用ObjectMapper将ResponseMsg对象转化为JSON字符串
            PrintWriter writer = response.getWriter();
            writer.print(json);  // 将JSON字符串写入响应输出流
            writer.flush();//刷新
            writer.close();//关闭
        } catch (Exception e) {
            e.printStackTrace();  // 打印异常堆栈信息
        }
    }
}

2.4 跨域配置CrossConfig

package com.aaa.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CrossConfig {
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        //corsConfiguration.setAllowCredentials(true);允许携带cookie
        corsConfiguration.addAllowedHeader("*"); // 允许所有的头
        corsConfiguration.addAllowedOrigin("*"); // 允许所有的请求源
        corsConfiguration.addAllowedMethod("*");  // 所欲的方法   get post delete put
        source.registerCorsConfiguration("/**", corsConfiguration); // 所有的路径都允许跨域
        return new CorsFilter(source);
    }

}

2.5 SecurityConfig 配置类

package com.aaa.config;

import cn.hutool.jwt.JWTUtil;
import com.aaa.filter.JWTFilter;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    protected UserDetailsService userDetailsService;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Resource
    public JWTFilter jwtFilter;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //.usernameParameter("username")//登陆账号
        //.passwordParameter("userpassword");//登陆密码
        //.defaultSuccessUrl("/test");//登陆成功的跳转路径


        // 配置登录页面和登录处理路径
        http.formLogin().loginPage("/login.html")//路径前面必须加  /
                .loginProcessingUrl("/login")//跟提交的路径一样
                // 登录成功的处理逻辑
                .successHandler((request, response, authentication) ->{
                    Collection authorities = authentication.getAuthorities();//获取资源信息
                    List resources = authorities.stream().map(auth -> auth.getAuthority()).collect(Collectors.toList());//转为stream流变成list
                    Map map = new HashMap<>();
                    map.put("username",authentication.getName());
                    map.put("resources",resources);
                    //生成token
                    String token = JWTUtil.createToken(map, "user".getBytes());
                    ResponseMsg responseMsg = new ResponseMsg(200,"登陆成功",token);
                    //响应返回信息
                    printJsonData(response,responseMsg);
                })
                // 登录失败的处理逻辑
                .failureHandler((request, response, exception)->{
                    ResponseMsg responseMsg = new ResponseMsg(400,"验证失败");
                    printJsonData(response,responseMsg);
                });

        // 配置权限不允许的处理逻辑
        http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
            ResponseMsg responseMsg = new ResponseMsg(403,"权限不允许",null);
            printJsonData(response,responseMsg);
        });

        // 配置不需要验证的路径
        http.authorizeRequests().antMatchers("/login.html", "login").permitAll();//不用验证
        //http.authorizeRequests().antMatchers("/test").hasRole("test");//必须有哪个权限才能访问
        //http.authorizeRequests().antMatchers("/test").hasAnyAuthority("resource");//必须有哪个资源才能访问

        // 配置其他路径需要验证
        http.authorizeRequests().anyRequest().authenticated();

        // 添加JWT过滤器
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);

        //关闭csrf保护
        http.csrf().disable();

        // 配置跨域
        http.cors();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }


    public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {

        try {
            response.setContentType("application/json;charset=utf8");  // json格式   编码是中文
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(responseMsg);// 使用ObjectMapper将result转化json为字符串
            PrintWriter writer = response.getWriter();
            writer.print(s);
            writer.flush();
            writer.close();
        }catch (Exception e){
            e.printStackTrace();

        }
    }
}

2.6 SecurityService类

也是跟上一篇的笔记一样

三、创建前端vue项目

login页面

不要忘了在路由下添加login页面

SpringbootSecurity登陆验证(前后端分离)_第2张图片



在main.js添加下面的代码

验证token是否存在,如果有的话就在请求头里面添加token

// 请求拦截器
instance.interceptors.request.use(config => {
    if (sessionStorage.getItem("token")){
        let  token = sessionStorage.getItem("token");
        config.headers['token'] = token;
    }
    return config;
}, error => {
    return Promise.reject(error);
});

四、总结

从前端页面输入账号向后端发送跨域请求,后端接受参数验证是否在数据库中存在,如果存在就去把用户在数据库中的角色权限和资源权限查出来,保存在Security中.登陆成功后就把用户的信息用JWTUtil.createToken()方法生成token,返回给前端,前端拿到token,前端再次向后端发起请求时携带token,后端识别,如果正确的话就放行,不正确就访问失败



你可能感兴趣的:(前端,后端,vue,spring,boot)