最近在做一个前后端分离项目,研究了好几天终于搞出来了,在此跟小伙伴分项一下,同时记录一下方便以后自己查阅,系统架构用的是SpringBoot2+SpringSecurity+JWT+VUE(ElementUI) 。
做大屏数据可视化,跟后台业务token 分开,将大屏配置表信息存到redis中,通过token 可以查询到。 用户访问大屏后台接口要带上Authorization ,访问前先校验token。校验通过则可以执行接口,并返回token 里存储的信息给接口调用。
大屏数据可视化有专门配置表:
redis 存储格式:large_screen_login_tokens:六位随机码:UUID
需要实现从浏览器输入六位随机码后先从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