Spring——Security前后分离校验token

2023-09-13 08:45:41

前言

之前采取项目中嵌套html页面,实现基本的登录校验权限校验登出操作记住我等功能试下。

但是,现在的开发基本都是前后分离样式,后端并不需要配置登录页的操作。

如何才能做到前后分离,同时也能支持登录token校验呢,本篇博客详细说明。

token配置

本次token生成采取jwt的方式。

为什么会考虑使用jwt,主要是因为平时开发中,采取数据库增加token字段,通过token查询数据库获取对应用户信息。

但是jwt本身就具有将信息转换为token,同时也能根据token转换信息的功能,能尽量缩减放问数据库的频次,提升数据查询处理效率。

引入JWT依赖文件

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

配置token管理器类

自定一个Token生成和从token中解析用户名的一个类,并交给Spring管理。

package security.config;import com.auth0.jwt.JWT;import com.auth0.jwt.JWTVerifier;import com.auth0.jwt.algorithms.Algorithm;import com.auth0.jwt.interfaces.Claim;import com.auth0.jwt.interfaces.DecodedJWT;import org.springframework.stereotype.Component;import java.util.Calendar;import java.util.HashMap;@ComponentpublicclassTokenJwtManager{// 设置token时间privateint tokenEcpiration=24*60*60*1000;// 毫秒   24h// 编码密钥private String tokenSignKey="123456";// 1、根据用户名生成tokenpublic StringcreateToken(String userName){
        Calendar calendar= Calendar.getInstance();
        calendar.add(Calendar.SECOND, tokenEcpiration);

        String userName1= JWT.create().withHeader(newHashMap<>()).withClaim("userName", userName).withExpiresAt(calendar.getTime())// 过期时间.sign(Algorithm.HMAC256(tokenSignKey));// 签名return userName1;}// 2、根据token得到用户名信息public StringgetUserName(String token){
        JWTVerifier build= JWT.require(Algorithm.HMAC256(tokenSignKey)).build();
        DecodedJWT verify= build.verify(token);
        Claim userName= verify.getClaim("userName");return userName.asString();}publicstaticvoidmain(String[] args){
        String ss=newTokenJwtManager().createToken("1111111");
        System.out.println(ss);
        System.out.println(newTokenJwtManager().getUserName(ss));}}

security 配置

配置未登录处理类

package security.config.handler;import lombok.extern.slf4j.Slf4j;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.stereotype.Component;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.HashMap;import java.util.Map;/**
 * 未登录
 */@Component@Slf4jpublicclassMyUnAuthEntryPointimplementsAuthenticationEntryPoint{@Overridepublicvoidcommence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)throws IOException, ServletException{
        log.info("======= commence ===");// 返回请求端
        Map<String,Object> resultMap=newHashMap<>();// 保存数据
        resultMap.put("code","10000");
        resultMap.put("msg","当前账户未登录");
        resultMap.put("data",newHashMap<>());// 设置返回消息类型
        httpServletResponse.setHeader("Content-type","text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");// 返回给请求端
        PrintWriter writer= httpServletResponse.getWriter();
        writer.write(resultMap.toString());
        writer.close();}}

配置无权限处理类

package security.config.handler;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.access.AccessDeniedHandler;import org.springframework.stereotype.Component;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.HashMap;import java.util.Map;/**
 * 无权访问配置(前后分离)
 */@Component// 交给spring管理publicclassMyAccessDeniedHandlerimplementsAccessDeniedHandler{@Overridepublicvoidhandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e)throws IOException, ServletException{
        Map<String,Object> resultMap=newHashMap<>();// 保存数据
        resultMap.put("code","403");
        resultMap.put("msg","无权访问");
        resultMap.put("data",null);// 设置返回消息类型
        httpServletResponse.setHeader("Content-type","text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");// 返回给请求端
        PrintWriter writer= httpServletResponse.getWriter();
        writer.write(resultMap.toString());
        writer.flush();
        writer.close();}}

配置登出操作处理类

package security.config.handler;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.logout.LogoutHandler;import org.springframework.stereotype.Component;import security.config.TokenJwtManager;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;/**
 * 登出
 */@Component@Slf4jpublicclassMyLogoutHandlerimplementsLogoutHandler{@Autowiredprivate TokenJwtManager tokenJwtManager;//    public MyLogoutHandler(TokenJwtManager tokenJwtManager) {//        this.tokenJwtManager = tokenJwtManager;//    }@Overridepublicvoidlogout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication){// 1、从header中获取token
        String token= httpServletRequest.getHeader("token");
        log.info("token信息为  {}",token);
        String userName= tokenJwtManager.getUserName(token);
        log.info("从token获取userName信息为  {}",token);// redis 移除登录信息等逻辑// xxxxx// 2、返回请求端
        Map<String,Object> resultMap=newHashMap<>();// 保存数据
        resultMap.put("code","200");
        resultMap.put("msg",userName+"登出成功");
        resultMap.put("data",newHashMap<>());// 设置返回消息类型
        httpServletResponse.setHeader("Content-type","text/html;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType("application/json;charset=UTF-8");// 返回给请求端
        PrintWriter writer= null;try{
            writer= httpServletResponse.getWriter();
            writer.write(resultMap.toString());
            writer.flush();
            writer.close();}catch(IOException e){
           e.printStackTrace();}}}

配置token认证过滤器

package security.filter;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.stereotype.Component;import security.config.TokenJwtManager;import security.vo.SecurityUser;import security.vo.User;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.ArrayList;import java.util.HashMap;import java.util.Map;// 这里交给spring管理会报错@Slf4jpublicclassTokenLoginFilterextendsUsernamePasswordAuthenticationFilter{private TokenJwtManager tokenJwtManager;private AuthenticationManager authenticationManager;publicTokenLoginFilter(TokenJwtManager tokenJwtManager, AuthenticationManager authenticationManager){this.tokenJwtManager= tokenJwtManager;this.authenticationManager= authenticationManager;this.setPostOnly(false);// 关闭登录只允许 post// 设置登陆路径,并且post请求this.setRequiresAuthenticationRequestMatcher(newAntPathRequestMatcher("/user/login","POST"));}// 1、获取登录页传递来的账户和密码信息@Overridepublic AuthenticationattemptAuthentication(HttpServletRequest request, HttpServletResponse response){
        log.info("==== attemptAuthentication  ======");

        String userName= request.getParameter("userName");
        String pwd= request.getParameter("passWord");
        log.info("userName:{},pwd:{}",userName,pwd);// 登录接口 /user/login 调用请求时触发// UsernamePasswordAuthenticationToken 封装登录时传递来的数据信息// 交给 AuthenticationManager  进行登录认证校验return authenticationManager.authenticate(newUsernamePasswordAuthenticationToken(userName,
                pwd,newArrayList<>()));}// 2、认证成功调用@AutowiredprotectedvoidsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)throws IOException, ServletException{
        log.info("==== successfulAuthentication  ======");// 认证成功之后,获取认证后的用户基本信息
        SecurityUser securityUser=(SecurityUser) authResult.getPrincipal();// 根据用户名生成对应的token
        String token= tokenJwtManager.createToken(securityUser.getUsername());// token信息存于redis、数据库、缓存等// 返回成功
        Map<String,Object> resultMap=newHashMap<>();// 保存数据
        resultMap.put("code","200");
        resultMap.put("msg","登录成功");
        resultMap.put("data",token);// 设置返回消息类型
        response.setHeader("Content-type","text/html;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");// 返回给请求端
        PrintWriter writer= response.getWriter();
        writer.write(resultMap.toString());
        writer.flush();
        writer.close();}// 3、认证失败调用的方法protectedvoidunsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)throws IOException, ServletException{
        log.info("==== unsuccessfulAuthentication  ======");
        Map<String,Object> resultMap=newHashMap<>();// 保存数据
        resultMap.put("code","500");
        resultMap.put("msg","登录验证失败");
        resultMap.put("data",newHashMap<>());// 设置返回消息类型
        response.setHeader("Content-type","text/html;charset=UTF-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");// 返回给请求端
        PrintWriter writer= response.getWriter();
        writer.write(resultMap.toString());
        writer.flush();
        writer.close();}}

配置token权限校验过滤器

package security.filter;import lombok.extern.slf4j.Slf4j;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.web.authentication.www.BasicAuthenticationF
  • 作者:专注写bug
  • 原文链接:https://writing-bugs.blog.csdn.net/article/details/123121463
    更新时间:2023-09-13 08:45:41