SpringBoot2+SpringSecurity+JWT+VUE(ElementUI) 支持自定义登陆Token 配置(六位随机码code) 艰苦配置历程

最近在做一个前后端分离项目,研究了好几天终于搞出来了,在此跟小伙伴分项一下,同时记录一下方便以后自己查阅,系统架构用的是SpringBoot2+SpringSecurity+JWT+VUE(ElementUI) 。

业务需求:

做大屏数据可视化,跟后台业务token 分开,将大屏配置表信息存到redis中,通过token 可以查询到。 用户访问大屏后台接口要带上Authorization ,访问前先校验token。校验通过则可以执行接口,并返回token 里存储的信息给接口调用。

大屏数据可视化有专门配置表:
SpringBoot2+SpringSecurity+JWT+VUE(ElementUI) 支持自定义登陆Token 配置(六位随机码code) 艰苦配置历程_第1张图片
redis 存储格式:large_screen_login_tokens:六位随机码:UUID
SpringBoot2+SpringSecurity+JWT+VUE(ElementUI) 支持自定义登陆Token 配置(六位随机码code) 艰苦配置历程_第2张图片

业务逻辑

需要实现从浏览器输入六位随机码后先从redis 里查询是否有token如果存在并且不超期,则直接将token 返回前端;如果redis 里不存在则重新生成,如果超期则刷新token,同时将大屏配置信息保存到redis 。

代码编写:

1.SecurityConfig 配置类:

package com.dechnic.framework.config;

import com.dechnic.framework.security.filter.JwtAuthenticationTokenFilter;
import com.dechnic.framework.security.handle.AuthenticationEntryPointImpl;
import com.dechnic.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;

/**
 * spring security配置
 *
 * @author ruoyi
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 退出处理类
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    /**
     * 跨域过滤器
     */
    @Autowired
    private CorsFilter corsFilter;

    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("/login", "/captchaImage").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                .antMatchers("/test/**").anonymous()
//                .antMatchers("/nh/LargeScreen/**").anonymous()
                .antMatchers("/nh/auth/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }


    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

2.JwtAuthenticationTokenFilter

package com.dechnic.framework.security.filter;

import com.dechnic.common.core.domain.model.LoginUser;
import com.dechnic.common.utils.SecurityUtils;
import com.dechnic.common.utils.StringUtils;
import com.dechnic.framework.web.service.TokenService;
import com.dechnic.nh.module.LoginLargeScreenUser;
import com.dechnic.nh.security.LargeScreenAuthenticationToken;
import com.dechnic.nh.service.impl.LargeScreenTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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;

/**
 * token过滤器 验证token有效性
 *
 * @author ruoyi
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    LargeScreenTokenService largeScreenTokenService;
    @Autowired
    private TokenService tokenService;
    private static final String largeScreenUrlPrifix = "nh/LargeScreen";
    private static final String [] passUrls = {"nh/auth/login"};

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        boolean passFlag = false;
        String reqPath = request.getRequestURI();
        for (String pass : passUrls){
            if (reqPath.indexOf(pass)!=-1){
                passFlag = true;
                break;
            }
        }
        if (passFlag){
            chain.doFilter(request, response);
        }else {
            int urlIndex = reqPath.indexOf(largeScreenUrlPrifix);
            boolean flag = true;
            if (urlIndex != -1) {
                flag = false;
            }

            if (flag) {
                LoginUser loginUser = tokenService.getLoginUser(request);
                if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
                    tokenService.verifyToken(loginUser);
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            } else {
                // 处理能耗大屏展示
                LoginLargeScreenUser loginLargeScreenUser = largeScreenTokenService.getLoginLargeScreenUser(request);
                if (StringUtils.isNotNull(loginLargeScreenUser)) {
                    largeScreenTokenService.verifyToken(loginLargeScreenUser);
                    LargeScreenAuthenticationToken largeScreenAuthenticationToken = new LargeScreenAuthenticationToken(loginLargeScreenUser,null);
                    largeScreenAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(largeScreenAuthenticationToken);

                }

            }

            chain.doFilter(request, response);
        }

    }
}

3.LargeScreenAuthenticationToken

package com.dechnic.nh.security;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @description:
 * @author:houqd
 * @time: 2021/12/29 15:15
 */

public class LargeScreenAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 1L;

    private final Object principal;


    public LargeScreenAuthenticationToken(Object principal) {
        super(null);
        this.principal = principal;
        setAuthenticated(false);
    }

    /**
     * Creates a token with the supplied array of authorities.
     *
     * @param authorities the collection of GrantedAuthoritys for the principal
     *                    represented by this authentication object.
     * @param principal
     */
    public LargeScreenAuthenticationToken(Object principal,Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);

    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }
        super.setAuthenticated(false);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }

}

4.AuthenticationEntryPointImpl

package com.dechnic.framework.security.handle;

import com.alibaba.fastjson.JSON;
import com.dechnic.common.constant.HttpStatus;
import com.dechnic.common.core.domain.AjaxResult;
import com.dechnic.common.utils.ServletUtils;
import com.dechnic.common.utils.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;

/**
 * 认证失败处理类 返回未授权
 *
 * @author ruoyi
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException {
        int code = HttpStatus.UNAUTHORIZED;
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

5.LogoutSuccessHandlerImpl

package com.dechnic.framework.security.handle;

import com.alibaba.fastjson.JSON;
import com.dechnic.common.constant.Constants;
import com.dechnic.common.constant.HttpStatus;
import com.dechnic.common.core.domain.AjaxResult;
import com.dechnic.common.core.domain.model.LoginUser;
import com.dechnic.common.utils.ServletUtils;
import com.dechnic.common.utils.StringUtils;
import com.dechnic.framework.manager.AsyncManager;
import com.dechnic.framework.manager.factory.AsyncFactory;
import com.dechnic.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义退出处理类 返回成功
 *
 * @author ruoyi
 */
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
    @Autowired
    private TokenService tokenService;

    /**
     * 退出处理
     *
     * @return
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser)) {
            String userName = loginUser.getUsername();
            // 删除用户缓存记录
            tokenService.delLoginUser(loginUser.getToken());
            // 记录用户退出日志
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
        }
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));
    }
}

6.大屏用户登陆LargeScreenAuthController

package com.dechnic.nh.controller;

import com.alibaba.fastjson.JSONObject;
import com.dechnic.common.core.domain.AjaxResult;
import com.dechnic.nh.service.ILargeScreenUserLoginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @description:
 * @author:houqd
 * @time: 2021/12/28 11:29
 */
@Api("大屏用户登陆")
@RestController
@RequestMapping("/nh/auth")
public class LargeScreenAuthController {
    @Autowired
    ILargeScreenUserLoginService largeScreenUserLoginService;

    @ApiOperation(value = "大屏用户登陆")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "uid", value = "uid", dataType = "String", required = true)
    })
    @GetMapping("/login")
    public AjaxResult login(@RequestParam("uid")String uid) {
        return largeScreenUserLoginService.login(uid);
    }



}

7.LargeScreenUserLoginServiceImpl

package com.dechnic.nh.service.impl;

import com.dechnic.common.constant.Constants;
import com.dechnic.common.constant.HttpStatus;
import com.dechnic.common.core.domain.AjaxResult;
import com.dechnic.common.utils.StringUtils;
import com.dechnic.nh.entity.SysUserLargescreen;
import com.dechnic.nh.module.LoginLargeScreenUser;
import com.dechnic.nh.service.ILargeScreenUserLoginService;
import com.dechnic.nh.service.ISysUserLargescreenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @description:
 * @author:houqd
 * @time: 2021/12/28 14:22
 */
@Service
public class LargeScreenUserLoginServiceImpl implements ILargeScreenUserLoginService {
    @Autowired
    ISysUserLargescreenService sysUserLargescreenService;
    @Autowired
    LargeScreenTokenService largeScreenTokenService;
    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @Override
    public AjaxResult login(String uid) {
        if (StringUtils.isEmpty(uid)) {
            return AjaxResult.error(HttpStatus.FORBIDDEN,"uid 不能为空");
        }
        SysUserLargescreen sysUserLargescreen = sysUserLargescreenService.getSysUserLargeScreenLoginByUid(uid);
        if (null == sysUserLargescreen){
            return AjaxResult.error(HttpStatus.FORBIDDEN,"uid 不存在");
        }
        Date validateTime = sysUserLargescreen.getValidateTime();
        Instant instant = validateTime.toInstant();
        LocalDateTime validTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
        LocalDateTime now = LocalDateTime.now();
        if (validTime.isBefore(now)){
            return AjaxResult.error(HttpStatus.FORBIDDEN,"ValidateTime 已超期");
        }
        LoginLargeScreenUser loginLargeScreenUser = new LoginLargeScreenUser();
        loginLargeScreenUser.setSysUserLargescreen(sysUserLargescreen);
        loginLargeScreenUser.setUid(uid);

        String token = this.largeScreenTokenService.getRedisExitsTokenByUid(uid);
        if (StringUtils.isEmpty(token)){
            // 创建新token
             token = largeScreenTokenService.createToken(loginLargeScreenUser);
        }
        loginLargeScreenUser.setToken(token);

        AjaxResult ajax = AjaxResult.success();
        ajax.put(Constants.TOKEN,token);
        ajax.put("largeScreenInfo",loginLargeScreenUser);

        return ajax;
    }
}

8.LargeScreenTokenService

package com.dechnic.nh.service.impl;

import com.dechnic.common.constant.Constants;
import com.dechnic.common.core.redis.RedisCache;
import com.dechnic.common.utils.ServletUtils;
import com.dechnic.common.utils.StringUtils;
import com.dechnic.common.utils.ip.AddressUtils;
import com.dechnic.common.utils.ip.IpUtils;
import com.dechnic.common.utils.uuid.IdUtils;
import com.dechnic.nh.module.LoginLargeScreenUser;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * token验证处理
 *
 * @author ruoyi
 */
@Component
public class LargeScreenTokenService {
    protected static final long MILLIS_SECOND = 1000;
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
    private static final Long MILLIS_MINUTE_TEN = 30 * 60 * 1000L;
    // 令牌自定义标识
    @Value("${stoken.header}")
    private String header;
    // 令牌秘钥
    @Value("${stoken.secret}")
    private String secret;
    // 令牌有效期(默认30分钟)
    @Value("${stoken.expireTime}")
    private int expireTime;
    @Autowired
    private RedisCache redisCache;

    /**
     * 获取大屏用户身份信息
     *
     * @return 用户信息
     */
    public LoginLargeScreenUser getLoginLargeScreenUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token)) {
            Claims claims = parseToken(token);
            // 解析对应的权限以及用户信息
            String uuid = (String) claims.get(Constants.LARGE_SCREEN_KEY);
            String lsKey = getTokenKey(uuid);
            LoginLargeScreenUser loginLargeScreenUser = redisCache.getCacheObject(lsKey);
            return loginLargeScreenUser;
        }
        return null;
    }

    /**
     * 设置大屏用户身份信息
     */
    public void setLoginLargeScreen(LoginLargeScreenUser loginLargeScreenUser) {
        if (StringUtils.isNotNull(loginLargeScreenUser) && StringUtils.isNotEmpty(loginLargeScreenUser.getToken())) {
            refreshToken(loginLargeScreenUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {
            String userKey = getTokenKey(token);
            redisCache.deleteObject(userKey);
        }
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginLargeScreenUser loginUser) {
        String token = StringUtils.isNotEmpty(loginUser.getToken())?loginUser.getToken():IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LARGE_SCREEN_KEY, loginUser.getUid() + ":" + token);
        return createToken(claims);
    }

    /**
     * 验证令牌有效期,相差不足30分钟,自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginLargeScreenUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginLargeScreenUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getUid() + ":" + loginUser.getToken());
        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginLargeScreenUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims) {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.getSubject();
    }


    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    private String getTokenKey(String uuid) {
        return Constants.LARGE_SCREEN_LOGIN_TOKEN_KEY + uuid;
    }

    /**
     * 根据uid 获取redis 里已缓存的登陆信息
     * @param uid
     * @return
     */
    public String getRedisExitsTokenByUid(String uid) {
        Collection<String> keys = redisCache.keys(Constants.LARGE_SCREEN_LOGIN_TOKEN_KEY + uid+":*");
        if (null != keys && keys.size() > 0) {
            for (String key : keys) {
                LoginLargeScreenUser loginLargeScreenUser = redisCache.getCacheObject(key);
                String token = createToken(loginLargeScreenUser);
                return token;
            }
        }

        return null;
    }


}

9.application.yml

# 项目相关配置
dechnic:
  # 名称
  name: tfoms
  # 版本
  version: 1.0.0
  # 版权年份
  copyrightYear: 2021
  # 实例演示开关
  demoEnabled: true
  # 文件路径 示例( Windows配置D:/dechnic/uploadPath,Linux配置 /home/dechnic/uploadPath)
  profile: D:/dechnic/uploadPath
  # 获取ip地址开关
  addressEnabled: false
  # 验证码类型 math 数组计算 char 字符验证
  captchaType: math



# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8090
  servlet:
    # 应用的访问路径
    context-path: /bem_api
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # tomcat最大线程数,默认为200
    max-threads: 800
    # Tomcat启动初始化的线程数,默认值25
    min-spare-threads: 30

# 日志配置
logging:
  level:
    com.dechnic: debug
    org.springframework: warn

# Spring配置
spring:
  # 资源信息
  messages:
    # 国际化资源文件路径
    basename: i18n/messages
  profiles:
    active: test
  # 文件上传
  servlet:
    multipart:
      # 单个文件大小
      max-file-size:  10MB
      # 设置总上传的文件大小
      max-request-size:  20MB
  # 服务模块
  devtools:
    restart:
      # 热部署开关
      enabled: true



# token配置
token:
  # 令牌自定义标识
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认30分钟)
  expireTime: 30

# 大屏展示 stoken配置
stoken:
  # 令牌自定义标识
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认30分钟)
  expireTime: 60000000

# MyBatis配置
mybatis:
  # 搜索指定包别名
  typeAliasesPackage: com.dechnic.**.entity,com.dechnic.**.domain
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # 加载全局的配置文件
  configLocation: classpath:mybatis/mybatis-config.xml

# PageHelper分页插件
pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

# Swagger配置
swagger:
  # 是否开启swagger
  enabled: true
  # 请求前缀
  pathMapping: /

# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: /system/notice/*
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*,/nh/*

前端:

<template>
  <p>大屏跳转</p>
</template>

<script>
  import {largeScreenLogin} from '@/api/nh/auth'
  import { setLargescreenToken } from '@/utils/auth'
  /*let goToBi = function() {
    if (this.uid === 'uIy4G2') {
      window.location.href = '/LargeScreen'
    } else {
      // window.location.href = 'login'
    }
  }*/
  export default {
    name: 'showbi',
    data() {
      return {
          // uid:this.$route.query.uid
          uid:'RXQb3S'
      }
    },
    created() {

    },
    mounted() {
      this.goToBi()
    },
    methods: {
      goToBi(){
        // 此处调用后台接口
        largeScreenLogin(this.uid).then(result=>{
          if(result.code == 200){
            setLargescreenToken(result.token)
            window.location.href = result.largeScreenInfo.sysUserLargescreen.bigscreenUrl
          }
        })

      }
    }
  }
</script>

<style lang="scss" scoped>
  .icons-container {
    margin: 10px 20px 0;
    overflow: hidden;

    .icon-item {
      margin: 20px;
      height: 85px;
      text-align: center;
      width: 100px;
      float: left;
      font-size: 30px;
      color: #24292e;
      cursor: pointer;
    }

    span {
      display: block;
      font-size: 16px;
      margin-top: 10px;
    }

    .disabled {
      pointer-events: none;
    }
  }
</style>

auth.js 将token 存到cookie

import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token' 

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

const LargescreenTokenKey = 'Largescreen-Token'

export function getLargescreenToken() {
  return Cookies.get(LargescreenTokenKey)
}

export function setLargescreenToken(token) {
  return Cookies.set(LargescreenTokenKey, token)
}

export function removeLargescreenToken() {
  return Cookies.remove(LargescreenTokenKey)
}

前端调用后台接口:

import request from '@/utils/request'
// 大屏用户登陆
export function largeScreenLogin(uid) {
  return request({
    url: '/nh/auth/login',
    method: 'get',
    params:{uid:uid}
  })
}

小结:
1.JwtAuthenticationTokenFilter 继承OncePerRequestFilter,每一次请求都会经过此过滤器,根据请求路径过滤掉不需要鉴权的登陆路径,然后根据大屏接口路径区分不同的token 鉴权,注意此处要用:
LargeScreenAuthenticationToken largeScreenAuthenticationToken = new LargeScreenAuthenticationToken(loginLargeScreenUser,null);不然会鉴权失败。
2. 配置文件里 expireTime 此处时间必须大于零

# 大屏展示 stoken配置
stoken:
  # 令牌自定义标识
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认30分钟)
  expireTime: 60000000

3.LargeScreenTokenService 定义了一些生成/刷新token,获取存储对象的方法,并存储到redis。
4.ScurityConfig 里面只把 .antMatchers("/nh/auth/**").anonymous() 放开鉴权,
其他的都需要token 鉴权。
5. 前端 auth.js 里 定义两种 token :Admin-Token 和Largescreen-Token 并将其缓存到Cookie ,当调用后台接口时从里面获取

request.js

import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL:process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 60000
})
// request拦截器
service.interceptors.request.use(config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  if (getToken() && !isToken) {
    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  // get请求映射params参数
  if (config.method === 'get' && config.params) {
    let url = config.url + '?';
    for (const propName of Object.keys(config.params)) {
      const value = config.params[propName];
      var part = encodeURIComponent(propName) + "=";
      if (value !== null && typeof(value) !== "undefined") {
        if (typeof value === 'object') {
          for (const key of Object.keys(value)) {
            let params = propName + '[' + key + ']';
            var subPart = encodeURIComponent(params) + "=";
            url += subPart + encodeURIComponent(value[key]) + "&";
          }
        } else {
          url += part + encodeURIComponent(value) + "&";
        }
      }
    }
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
  }
  return config
}, error => {
    console.log(error)
    Promise.reject(error)
})

// 响应拦截器
service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    if (code === 401) {
      MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        store.dispatch('LogOut').then(() => {
          location.href = '/index';
        })
      })
    } else if (code === 500) {
      Message({
        message: msg,
        type: 'error'
      })
      return Promise.reject(new Error(msg))
    } else if (code !== 200) {
      Notification.error({
        title: msg
      })
      return Promise.reject('error')
    } else {
      return res.data
    }
  },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    }
    else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    }
    else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    Message({
      message: message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

你可能感兴趣的:(SpringBoot,+VUE,系列,vue.js,elementui,前端,java,spring,boot)