Commit 5fab97cf7e49636d446e063ee6e0eea80c124bd2

Authored by 648540858
1 parent 95688e40

支持不同域的前后端分离部署

src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
@@ -14,7 +14,8 @@ import org.springframework.stereotype.Component; @@ -14,7 +14,8 @@ import org.springframework.stereotype.Component;
14 import org.springframework.util.ObjectUtils; 14 import org.springframework.util.ObjectUtils;
15 import org.springframework.web.filter.OncePerRequestFilter; 15 import org.springframework.web.filter.OncePerRequestFilter;
16 16
17 -import javax.servlet.*; 17 +import javax.servlet.FilterChain;
  18 +import javax.servlet.ServletException;
18 import javax.servlet.annotation.WebFilter; 19 import javax.servlet.annotation.WebFilter;
19 import javax.servlet.http.HttpServletRequest; 20 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse; 21 import javax.servlet.http.HttpServletResponse;
@@ -48,13 +49,6 @@ public class ApiAccessFilter extends OncePerRequestFilter { @@ -48,13 +49,6 @@ public class ApiAccessFilter extends OncePerRequestFilter {
48 long start = System.currentTimeMillis(); // 请求进入时间 49 long start = System.currentTimeMillis(); // 请求进入时间
49 String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI()); 50 String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI());
50 51
51 - String origin = servletRequest.getHeader("Origin");  
52 - servletResponse.setContentType("application/json;charset=UTF-8");  
53 - servletResponse.setHeader("Access-Control-Allow-Origin", origin != null ? origin : "*");  
54 - servletResponse.setHeader("Access-Control-Allow-Credentials", "true");  
55 - servletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");  
56 - servletResponse.setHeader("Access-Control-Max-Age", "3600");  
57 - servletResponse.setHeader("Access-Control-Allow-Headers", "token,Content-Type,Content-Length, Authorization, Accept,X-Requested-With,domain,zdy");  
58 filterChain.doFilter(servletRequest, servletResponse); 52 filterChain.doFilter(servletRequest, servletResponse);
59 53
60 if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) { 54 if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) {
src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
@@ -32,6 +32,17 @@ public class GlobalExceptionHandler { @@ -32,6 +32,17 @@ public class GlobalExceptionHandler {
32 return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage()); 32 return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
33 } 33 }
34 34
  35 + /**
  36 + * 默认异常处理
  37 + * @param e 异常
  38 + * @return 统一返回结果
  39 + */
  40 + @ExceptionHandler(IllegalStateException.class)
  41 + @ResponseStatus(HttpStatus.BAD_REQUEST)
  42 + public WVPResult<String> exceptionHandler(IllegalStateException e) {
  43 + return WVPResult.fail(ErrorCode.ERROR400);
  44 + }
  45 +
35 46
36 /** 47 /**
37 * 自定义异常处理, 处理controller中返回的错误 48 * 自定义异常处理, 处理controller中返回的错误
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -55,6 +55,8 @@ public class UserSetting { @@ -55,6 +55,8 @@ public class UserSetting {
55 55
56 private List<String> interfaceAuthenticationExcludes = new ArrayList<>(); 56 private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
57 57
  58 + private List<String> allowedOrigins = new ArrayList<>();
  59 +
58 public Boolean getSavePositionHistory() { 60 public Boolean getSavePositionHistory() {
59 return savePositionHistory; 61 return savePositionHistory;
60 } 62 }
@@ -218,4 +220,12 @@ public class UserSetting { @@ -218,4 +220,12 @@ public class UserSetting {
218 public void setSipLog(Boolean sipLog) { 220 public void setSipLog(Boolean sipLog) {
219 this.sipLog = sipLog; 221 this.sipLog = sipLog;
220 } 222 }
  223 +
  224 + public List<String> getAllowedOrigins() {
  225 + return allowedOrigins;
  226 + }
  227 +
  228 + public void setAllowedOrigins(List<String> allowedOrigins) {
  229 + this.allowedOrigins = allowedOrigins;
  230 + }
221 } 231 }
src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
@@ -28,15 +28,6 @@ public class AnonymousAuthenticationEntryPoint implements AuthenticationEntry @@ -28,15 +28,6 @@ public class AnonymousAuthenticationEntryPoint implements AuthenticationEntry
28 String username = jwtUser.getUserName(); 28 String username = jwtUser.getUserName();
29 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() ); 29 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
30 SecurityContextHolder.getContext().setAuthentication(token); 30 SecurityContextHolder.getContext().setAuthentication(token);
31 - System.out.println(jwt);  
32 - // 允许跨域  
33 - String origin = request.getHeader("Origin");  
34 - response.setHeader("Access-Control-Allow-Credentials", "true");  
35 - response.setHeader("Access-Control-Allow-Origin", origin != null ? origin : "*");  
36 - response.setHeader("Access-Control-Allow-Methods", "PUT,POST, GET,DELETE,OPTIONS");  
37 - // 允许自定义请求头token(允许head跨域)  
38 - response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");  
39 - response.setHeader("Content-type", "application/json;charset=UTF-8");  
40 JSONObject jsonObject = new JSONObject(); 31 JSONObject jsonObject = new JSONObject();
41 jsonObject.put("code", ErrorCode.ERROR401.getCode()); 32 jsonObject.put("code", ErrorCode.ERROR401.getCode());
42 jsonObject.put("msg", ErrorCode.ERROR401.getMsg()); 33 jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
@@ -24,15 +24,24 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { @@ -24,15 +24,24 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
24 24
25 @Override 25 @Override
26 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { 26 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
  27 +
  28 + // 忽略登录请求的token验证
  29 + String requestURI = request.getRequestURI();
  30 + if (requestURI.equalsIgnoreCase("/api/user/login")) {
  31 + chain.doFilter(request, response);
  32 + return;
  33 + }
27 String jwt = request.getHeader(JwtUtils.getHeader()); 34 String jwt = request.getHeader(JwtUtils.getHeader());
28 // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 35 // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
29 // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 36 // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
30 if (StringUtils.isBlank(jwt)) { 37 if (StringUtils.isBlank(jwt)) {
31 - chain.doFilter(request, response);  
32 - return; 38 + jwt = request.getParameter(JwtUtils.getHeader());
  39 + if (StringUtils.isBlank(jwt)) {
  40 + chain.doFilter(request, response);
  41 + return;
  42 + }
33 } 43 }
34 44
35 -  
36 JwtUser jwtUser = JwtUtils.verifyToken(jwt); 45 JwtUser jwtUser = JwtUtils.verifyToken(jwt);
37 String username = jwtUser.getUserName(); 46 String username = jwtUser.getUserName();
38 // TODO 处理各个状态 47 // TODO 处理各个状态
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
@@ -23,7 +23,7 @@ public class JwtUtils { @@ -23,7 +23,7 @@ public class JwtUtils {
23 23
24 private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); 24 private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
25 25
26 - private static final String HEADER = "Access-Token"; 26 + private static final String HEADER = "access-token";
27 private static final String AUDIENCE = "Audience"; 27 private static final String AUDIENCE = "Audience";
28 28
29 private static final long EXPIRED_THRESHOLD = 10 * 60; 29 private static final long EXPIRED_THRESHOLD = 10 * 60;
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
@@ -27,16 +27,13 @@ public class SecurityUtils { @@ -27,16 +27,13 @@ public class SecurityUtils {
27 public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { 27 public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
28 //使用security框架自带的验证token生成器 也可以自定义。 28 //使用security框架自带的验证token生成器 也可以自定义。
29 UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password); 29 UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
30 -// Authentication authenticate = authenticationManager.authenticate(token);  
31 -// SecurityContextHolder.getContext().setAuthentication(authenticate);  
32 - SecurityContextHolder.getContext().setAuthentication(token); 30 + //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
  31 + Authentication authenticate = authenticationManager.authenticate(token);
  32 + LoginUser user = (LoginUser) authenticate.getPrincipal();
33 33
  34 + SecurityContextHolder.getContext().setAuthentication(token);
34 35
35 -// LoginUser user = (LoginUser) authenticate.getPrincipal();  
36 - User user = new User();  
37 - user.setUsername(username);  
38 - LoginUser loginUser = new LoginUser(user, LocalDateTime.now());  
39 - return loginUser; 36 + return user;
40 } 37 }
41 38
42 /** 39 /**
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
@@ -18,8 +18,13 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @@ -18,8 +18,13 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
18 import org.springframework.security.config.http.SessionCreationPolicy; 18 import org.springframework.security.config.http.SessionCreationPolicy;
19 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 19 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
20 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 20 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  21 +import org.springframework.web.cors.CorsConfiguration;
  22 +import org.springframework.web.cors.CorsConfigurationSource;
  23 +import org.springframework.web.cors.CorsUtils;
  24 +import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
21 25
22 -import java.util.List; 26 +import java.util.ArrayList;
  27 +import java.util.Arrays;
23 28
24 /** 29 /**
25 * 配置Spring Security 30 * 配置Spring Security
@@ -61,12 +66,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -61,12 +66,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
61 @Autowired 66 @Autowired
62 private JwtAuthenticationFilter jwtAuthenticationFilter; 67 private JwtAuthenticationFilter jwtAuthenticationFilter;
63 68
64 -// @Bean  
65 -// JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {  
66 -// JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());  
67 -// return jwtAuthenticationFilter;  
68 -// }  
69 -  
70 69
71 /** 70 /**
72 * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链 71 * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
@@ -77,27 +76,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -77,27 +76,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
77 if (!userSetting.isInterfaceAuthentication()) { 76 if (!userSetting.isInterfaceAuthentication()) {
78 web.ignoring().antMatchers("**"); 77 web.ignoring().antMatchers("**");
79 }else { 78 }else {
  79 + ArrayList<String> matchers = new ArrayList<>();
  80 + matchers.add("/");
  81 + matchers.add("/#/**");
  82 + matchers.add("/static/**");
  83 + matchers.add("/index.html");
  84 + matchers.add("/doc.html");
  85 + matchers.add("/webjars/**");
  86 + matchers.add("/swagger-resources/**");
  87 + matchers.add("/v3/api-docs/**");
  88 + matchers.add("/js/**");
  89 + matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
80 // 可以直接访问的静态数据 90 // 可以直接访问的静态数据
81 - web.ignoring()  
82 - .antMatchers("/")  
83 - .antMatchers("/#/**")  
84 - .antMatchers("/static/**")  
85 - .antMatchers("/index.html")  
86 - .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"  
87 - .antMatchers("/webjars/**")  
88 - .antMatchers("/swagger-resources/**")  
89 - .antMatchers("/v3/api-docs/**")  
90 - .antMatchers("/favicon.ico")  
91 - .antMatchers("/js/**");  
92 - List<String> interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes();  
93 - for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {  
94 - if (interfaceAuthenticationExclude.split("/").length < 4 ) {  
95 - logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);  
96 - }else {  
97 - web.ignoring().antMatchers(interfaceAuthenticationExclude);  
98 - }  
99 -  
100 - } 91 + web.ignoring().antMatchers(matchers.toArray(new String[0]));
101 } 92 }
102 } 93 }
103 94
@@ -121,7 +112,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -121,7 +112,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
121 @Override 112 @Override
122 protected void configure(HttpSecurity http) throws Exception { 113 protected void configure(HttpSecurity http) throws Exception {
123 http.headers().contentTypeOptions().disable() 114 http.headers().contentTypeOptions().disable()
124 - .and().cors() 115 + .and().cors().configurationSource(configurationSource())
125 .and().csrf().disable() 116 .and().csrf().disable()
126 .sessionManagement() 117 .sessionManagement()
127 .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 118 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
@@ -129,50 +120,36 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -129,50 +120,36 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
129 // 配置拦截规则 120 // 配置拦截规则
130 .and() 121 .and()
131 .authorizeRequests() 122 .authorizeRequests()
  123 + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
  124 + .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
132 .antMatchers("/api/user/login","/index/hook/**").permitAll() 125 .antMatchers("/api/user/login","/index/hook/**").permitAll()
133 .anyRequest().authenticated() 126 .anyRequest().authenticated()
134 // 异常处理器 127 // 异常处理器
135 .and() 128 .and()
136 .exceptionHandling() 129 .exceptionHandling()
137 .authenticationEntryPoint(anonymousAuthenticationEntryPoint) 130 .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
138 -// .accessDeniedHandler(jwtAccessDeniedHandler)  
139 - // 配置自定义的过滤器  
140 -// .and()  
141 -// .addFilter(jwtAuthenticationFilter)  
142 - // 验证码过滤器放在UsernamePassword过滤器之前  
143 -// .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class) 131 + .and().logout().logoutUrl("/api/user/logout").permitAll()
  132 + .logoutSuccessHandler(logoutHandler)
144 ; 133 ;
145 http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); 134 http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
146 -// // 设置允许添加静态文件  
147 -// http.headers().contentTypeOptions().disable();  
148 -// http.authorizeRequests()  
149 -// // 放行接口  
150 -// .antMatchers("/api/user/login","/index/hook/**").permitAll()  
151 -// // 除上面外的所有请求全部需要鉴权认证  
152 -// .anyRequest().authenticated()  
153 -// // 禁用session  
154 -// .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)  
155 -// // 异常处理(权限拒绝、登录失效等)  
156 -// .and().exceptionHandling()  
157 -// // 匿名用户访问无权限资源时的异常处理  
158 -// .authenticationEntryPoint(anonymousAuthenticationEntryPoint)  
159 -// // 登录 允许所有用户  
160 -// .and().formLogin()  
161 -// // 登录成功处理逻辑 在这里给出JWT  
162 -// .successHandler(loginSuccessHandler)  
163 -// // 登录失败处理逻辑  
164 -// .failureHandler(loginFailureHandler)  
165 -// // 登出  
166 -// .and().logout().logoutUrl("/api/user/logout").permitAll()  
167 -// // 登出成功处理逻辑  
168 -// .logoutSuccessHandler(logoutHandler)  
169 -// // 配置自定义的过滤器  
170 -// .and()  
171 -// .addFilter(jwtAuthenticationFilter())  
172 -// ;  
173 135
174 } 136 }
175 137
  138 + CorsConfigurationSource configurationSource(){
  139 + // 配置跨域
  140 + CorsConfiguration corsConfiguration = new CorsConfiguration();
  141 + corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
  142 + corsConfiguration.setAllowedMethods(Arrays.asList("*"));
  143 + corsConfiguration.setMaxAge(3600L);
  144 + corsConfiguration.setAllowCredentials(true);
  145 + corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
  146 + corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
  147 +
  148 + UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
  149 + url.registerCorsConfiguration("/**",corsConfiguration);
  150 + return url;
  151 + }
  152 +
176 /** 153 /**
177 * 描述: 密码加密算法 BCrypt 推荐使用 154 * 描述: 密码加密算法 BCrypt 推荐使用
178 **/ 155 **/
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
@@ -28,6 +28,10 @@ public class WVPResult&lt;T&gt; implements Cloneable{ @@ -28,6 +28,10 @@ public class WVPResult&lt;T&gt; implements Cloneable{
28 return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t); 28 return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t);
29 } 29 }
30 30
  31 + public static WVPResult success() {
  32 + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null);
  33 + }
  34 +
31 public static <T> WVPResult<T> success(T t) { 35 public static <T> WVPResult<T> success(T t) {
32 return success(t, ErrorCode.SUCCESS.getMsg()); 36 return success(t, ErrorCode.SUCCESS.getMsg());
33 } 37 }
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java
@@ -31,7 +31,6 @@ import java.text.ParseException; @@ -31,7 +31,6 @@ import java.text.ParseException;
31 import java.util.UUID; 31 import java.util.UUID;
32 32
33 @Tag(name = "国标设备配置") 33 @Tag(name = "国标设备配置")
34 -  
35 @RestController 34 @RestController
36 @RequestMapping("/api/device/config") 35 @RequestMapping("/api/device/config")
37 public class DeviceConfig { 36 public class DeviceConfig {
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -24,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation; @@ -24,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation;
24 import io.swagger.v3.oas.annotations.Parameter; 24 import io.swagger.v3.oas.annotations.Parameter;
25 import io.swagger.v3.oas.annotations.tags.Tag; 25 import io.swagger.v3.oas.annotations.tags.Tag;
26 import org.apache.commons.compress.utils.IOUtils; 26 import org.apache.commons.compress.utils.IOUtils;
  27 +import org.apache.ibatis.annotations.Options;
27 import org.slf4j.Logger; 28 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.annotation.Autowired; 30 import org.springframework.beans.factory.annotation.Autowired;
@@ -97,8 +98,10 @@ public class DeviceQuery { @@ -97,8 +98,10 @@ public class DeviceQuery {
97 @Parameter(name = "page", description = "当前页", required = true) 98 @Parameter(name = "page", description = "当前页", required = true)
98 @Parameter(name = "count", description = "每页查询数量", required = true) 99 @Parameter(name = "count", description = "每页查询数量", required = true)
99 @GetMapping("/devices") 100 @GetMapping("/devices")
  101 + @Options()
100 public PageInfo<Device> devices(int page, int count){ 102 public PageInfo<Device> devices(int page, int count){
101 - 103 +// if (page == null) page = 0;
  104 +// if (count == null) count = 20;
102 return storager.queryVideoDeviceList(page, count,null); 105 return storager.queryVideoDeviceList(page, count,null);
103 } 106 }
104 107
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
@@ -23,7 +23,6 @@ import io.swagger.v3.oas.annotations.Parameter; @@ -23,7 +23,6 @@ import io.swagger.v3.oas.annotations.Parameter;
23 import io.swagger.v3.oas.annotations.tags.Tag; 23 import io.swagger.v3.oas.annotations.tags.Tag;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.beans.factory.annotation.Value; 25 import org.springframework.beans.factory.annotation.Value;
26 -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
27 import org.springframework.util.ObjectUtils; 26 import org.springframework.util.ObjectUtils;
28 import org.springframework.web.bind.annotation.*; 27 import org.springframework.web.bind.annotation.*;
29 28
@@ -71,9 +70,6 @@ public class ServerController { @@ -71,9 +70,6 @@ public class ServerController {
71 70
72 71
73 @Autowired 72 @Autowired
74 - private ThreadPoolTaskExecutor taskExecutor;  
75 -  
76 - @Autowired  
77 private IRedisCatchStorage redisCatchStorage; 73 private IRedisCatchStorage redisCatchStorage;
78 74
79 75
src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
@@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse; @@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse;
27 import java.util.List; 27 import java.util.List;
28 28
29 @Tag(name = "用户管理") 29 @Tag(name = "用户管理")
30 -  
31 @RestController 30 @RestController
32 @RequestMapping("/api/user") 31 @RequestMapping("/api/user")
33 public class UserController { 32 public class UserController {
@@ -47,7 +46,7 @@ public class UserController { @@ -47,7 +46,7 @@ public class UserController {
47 @Parameter(name = "username", description = "用户名", required = true) 46 @Parameter(name = "username", description = "用户名", required = true)
48 @Parameter(name = "password", description = "密码(32位md5加密)", required = true) 47 @Parameter(name = "password", description = "密码(32位md5加密)", required = true)
49 public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){ 48 public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){
50 - LoginUser user = null; 49 + LoginUser user;
51 try { 50 try {
52 user = SecurityUtils.login(username, password, authenticationManager); 51 user = SecurityUtils.login(username, password, authenticationManager);
53 } catch (AuthenticationException e) { 52 } catch (AuthenticationException e) {
@@ -62,6 +61,25 @@ public class UserController { @@ -62,6 +61,25 @@ public class UserController {
62 return user; 61 return user;
63 } 62 }
64 63
  64 +// @GetMapping("/logout")
  65 +// @PostMapping("/logout")
  66 +// @Operation(summary = "登出")
  67 +// public LoginUser logout(){
  68 +// LoginUser user;
  69 +// try {
  70 +// user = SecurityUtils.login(username, password, authenticationManager);
  71 +// } catch (AuthenticationException e) {
  72 +// throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage());
  73 +// }
  74 +// if (user == null) {
  75 +// throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误");
  76 +// }else {
  77 +// String jwt = JwtUtils.createToken(username, password);
  78 +// response.setHeader(JwtUtils.getHeader(), jwt);
  79 +// }
  80 +// return user;
  81 +// }
  82 +
65 @PostMapping("/changePassword") 83 @PostMapping("/changePassword")
66 @Operation(summary = "修改密码") 84 @Operation(summary = "修改密码")
67 @Parameter(name = "username", description = "用户名", required = true) 85 @Parameter(name = "username", description = "用户名", required = true)
src/main/resources/all-application.yml
@@ -201,6 +201,10 @@ user-settings: @@ -201,6 +201,10 @@ user-settings:
201 sip-log: true 201 sip-log: true
202 # 自动数据库升级,保证表结构完整 202 # 自动数据库升级,保证表结构完整
203 sync-db: true 203 sync-db: true
  204 + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
  205 + allowed-origins:
  206 + - http://localhost:8008
  207 + - http://192.168.1.3:8008
204 208
205 # 关闭在线文档(生产环境建议关闭) 209 # 关闭在线文档(生产环境建议关闭)
206 springdoc: 210 springdoc:
web_src/package-lock.json
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 "vue-clipboard2": "^0.3.1", 23 "vue-clipboard2": "^0.3.1",
24 "vue-clipboards": "^1.3.0", 24 "vue-clipboards": "^1.3.0",
25 "vue-contextmenujs": "^1.3.13", 25 "vue-contextmenujs": "^1.3.13",
26 - "vue-cookies": "^1.7.4", 26 + "vue-cookies": "^1.8.3",
27 "vue-giant-tree": "^0.1.5", 27 "vue-giant-tree": "^0.1.5",
28 "vue-router": "^3.1.6", 28 "vue-router": "^3.1.6",
29 "vue-ztree-2.0": "^1.0.4" 29 "vue-ztree-2.0": "^1.0.4"
@@ -13135,9 +13135,9 @@ @@ -13135,9 +13135,9 @@
13135 "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8=" 13135 "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8="
13136 }, 13136 },
13137 "node_modules/vue-cookies": { 13137 "node_modules/vue-cookies": {
13138 - "version": "1.7.4",  
13139 - "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz?cache=0&sync_timestamp=1598941352058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-cookies%2Fdownload%2Fvue-cookies-1.7.4.tgz",  
13140 - "integrity": "sha1-0kHQoEMdoHlYN2UdELTXPnyNPo0=" 13138 + "version": "1.8.3",
  13139 + "resolved": "https://registry.npmmirror.com/vue-cookies/-/vue-cookies-1.8.3.tgz",
  13140 + "integrity": "sha512-VBRsyRMVdahBgFfh389TMHPmDdr4URDJNMk4FKSCfuNITs7+jitBDhwyL4RJd3WUsfOYNNjPAkfbehyH9AFuoA=="
13141 }, 13141 },
13142 "node_modules/vue-giant-tree": { 13142 "node_modules/vue-giant-tree": {
13143 "version": "0.1.5", 13143 "version": "0.1.5",
@@ -25489,9 +25489,9 @@ @@ -25489,9 +25489,9 @@
25489 "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8=" 25489 "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8="
25490 }, 25490 },
25491 "vue-cookies": { 25491 "vue-cookies": {
25492 - "version": "1.7.4",  
25493 - "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz?cache=0&sync_timestamp=1598941352058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-cookies%2Fdownload%2Fvue-cookies-1.7.4.tgz",  
25494 - "integrity": "sha1-0kHQoEMdoHlYN2UdELTXPnyNPo0=" 25492 + "version": "1.8.3",
  25493 + "resolved": "https://registry.npmmirror.com/vue-cookies/-/vue-cookies-1.8.3.tgz",
  25494 + "integrity": "sha512-VBRsyRMVdahBgFfh389TMHPmDdr4URDJNMk4FKSCfuNITs7+jitBDhwyL4RJd3WUsfOYNNjPAkfbehyH9AFuoA=="
25495 }, 25495 },
25496 "vue-giant-tree": { 25496 "vue-giant-tree": {
25497 "version": "0.1.5", 25497 "version": "0.1.5",
web_src/package.json
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 "vue-clipboard2": "^0.3.1", 25 "vue-clipboard2": "^0.3.1",
26 "vue-clipboards": "^1.3.0", 26 "vue-clipboards": "^1.3.0",
27 "vue-contextmenujs": "^1.3.13", 27 "vue-contextmenujs": "^1.3.13",
28 - "vue-cookies": "^1.7.4", 28 + "vue-cookies": "^1.8.3",
29 "vue-giant-tree": "^0.1.5", 29 "vue-giant-tree": "^0.1.5",
30 "vue-router": "^3.1.6", 30 "vue-router": "^3.1.6",
31 "vue-ztree-2.0": "^1.0.4" 31 "vue-ztree-2.0": "^1.0.4"
web_src/src/App.vue
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 </template> 5 </template>
6 6
7 <script> 7 <script>
  8 +import userService from './components/service/UserService'
8 export default { 9 export default {
9 name: 'app', 10 name: 'app',
10 data(){ 11 data(){
@@ -19,7 +20,7 @@ export default { @@ -19,7 +20,7 @@ export default {
19 } 20 }
20 }, 21 },
21 created() { 22 created() {
22 - if(!this.$cookies.get("session")){ 23 + if (userService.getToken() == null){
23 //如果没有登录状态则跳转到登录页 24 //如果没有登录状态则跳转到登录页
24 this.$router.push('/login'); 25 this.$router.push('/login');
25 } 26 }
@@ -33,28 +34,14 @@ export default { @@ -33,28 +34,14 @@ export default {
33 // this.getUserInfo(); 34 // this.getUserInfo();
34 }, 35 },
35 methods: { 36 methods: {
36 - //请求用户的一些信息  
37 - getUserInfo(){  
38 - var userinfo = this.$cookies.get("session");  
39 - },  
40 checkLogin(){ 37 checkLogin(){
41 //检查是否存在session 38 //检查是否存在session
42 - //cookie操作方法在源码里有或者参考网上的即可  
43 - if(!this.$cookies.get("session")){ 39 + if (userService.getToken() == null){
44 //如果没有登录状态则跳转到登录页 40 //如果没有登录状态则跳转到登录页
45 - this.$router.push('/login'); 41 + // this.$router.push('/login');
46 } 42 }
  43 +
47 }, 44 },
48 - getCookie: function (cname) {  
49 - var name = cname + "=";  
50 - var ca = document.cookie.split(';');  
51 - for (var i = 0; i < ca.length; i++) {  
52 - var c = ca[i];  
53 - while (c.charAt(0) == ' ') c = c.substring(1);  
54 - if (c.indexOf(name) != -1) return c.substring(name.length, c.length);  
55 - }  
56 - return "";  
57 - }  
58 }, 45 },
59 components: {} 46 components: {}
60 }; 47 };
web_src/src/components/Login.vue
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 35
36 <script> 36 <script>
37 import crypto from 'crypto' 37 import crypto from 'crypto'
  38 +import userService from "./service/UserService";
38 export default { 39 export default {
39 name: 'Login', 40 name: 'Login',
40 data(){ 41 data(){
@@ -85,9 +86,10 @@ export default { @@ -85,9 +86,10 @@ export default {
85 params: loginParam 86 params: loginParam
86 }).then(function (res) { 87 }).then(function (res) {
87 window.clearTimeout(timeoutTask) 88 window.clearTimeout(timeoutTask)
88 - console.log(JSON.stringify(res)); 89 + console.log(res);
  90 + console.log("登录成功");
89 if (res.data.code === 0 ) { 91 if (res.data.code === 0 ) {
90 - that.$cookies.set("session", {"username": that.username,"roleId":res.data.data.role.id}) ; 92 + userService.setUser(res.data.data)
91 //登录成功后 93 //登录成功后
92 that.cancelEnterkeyDefaultAction(); 94 that.cancelEnterkeyDefaultAction();
93 that.$router.push('/'); 95 that.$router.push('/');
@@ -106,14 +108,6 @@ export default { @@ -106,14 +108,6 @@ export default {
106 that.isLoging = false; 108 that.isLoging = false;
107 }); 109 });
108 }, 110 },
109 - setCookie: function (cname, cvalue, exdays) {  
110 - var d = new Date();  
111 - d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));  
112 - var expires = "expires=" + d.toUTCString();  
113 - console.info(cname + "=" + cvalue + "; " + expires);  
114 - document.cookie = cname + "=" + cvalue + "; " + expires;  
115 - console.info(document.cookie);  
116 - },  
117 cancelEnterkeyDefaultAction: function() { 111 cancelEnterkeyDefaultAction: function() {
118 document.onkeydown = function(e) { 112 document.onkeydown = function(e) {
119 var key = window.event.keyCode; 113 var key = window.event.keyCode;
web_src/src/components/dialog/changePassword.vue
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 35
36 <script> 36 <script>
37 import crypto from 'crypto' 37 import crypto from 'crypto'
  38 +import userService from "../service/UserService";
38 export default { 39 export default {
39 name: "changePassword", 40 name: "changePassword",
40 props: {}, 41 props: {},
@@ -105,7 +106,7 @@ export default { @@ -105,7 +106,7 @@ export default {
105 this.showDialog = false; 106 this.showDialog = false;
106 setTimeout(()=>{ 107 setTimeout(()=>{
107 // 删除cookie,回到登录页面 108 // 删除cookie,回到登录页面
108 - this.$cookies.remove("session"); 109 + userService.clearUserInfo();
109 this.$router.push('/login'); 110 this.$router.push('/login');
110 this.sseSource.close(); 111 this.sseSource.close();
111 },800) 112 },800)
web_src/src/components/dialog/importChannel.vue
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 drag 16 drag
17 :action="uploadUrl" 17 :action="uploadUrl"
18 name="file" 18 name="file"
  19 + :headers="headers"
19 :on-success="successHook" 20 :on-success="successHook"
20 :on-error="errorHook" 21 :on-error="errorHook"
21 > 22 >
@@ -33,6 +34,8 @@ @@ -33,6 +34,8 @@
33 34
34 import ShowErrorData from './importChannelShowErrorData.vue' 35 import ShowErrorData from './importChannelShowErrorData.vue'
35 36
  37 +import userService from "../service/UserService";
  38 +
36 export default { 39 export default {
37 name: "importChannel", 40 name: "importChannel",
38 components: { 41 components: {
@@ -47,7 +50,10 @@ export default { @@ -47,7 +50,10 @@ export default {
47 isEdit: false, 50 isEdit: false,
48 errorStreams: [], 51 errorStreams: [],
49 errorGBIds: [], 52 errorGBIds: [],
50 - uploadUrl: process.env.NODE_ENV === 'development'?`debug/api/push/upload`:`api/push/upload`, 53 + headers: {
  54 + "access-token": userService.getToken()
  55 + },
  56 + uploadUrl: process.env.NODE_ENV === 'development'? `http://127.0.0.1:8080/debug/api/push/upload`: (window.baseUrl ? window.baseUrl : "") + `/api/push/upload`,
51 }; 57 };
52 }, 58 },
53 methods: { 59 methods: {
web_src/src/components/service/UserService.js 0 → 100644
  1 +
  2 +export default {
  3 +
  4 + /**
  5 + * 存储用户信息
  6 + * @param username
  7 + * @param token
  8 + */
  9 + setUser(user){
  10 + localStorage.setItem("wvp-user", JSON.stringify(user));
  11 + },
  12 +
  13 + /**
  14 + * 获取用户
  15 + */
  16 + getUser(){
  17 + return JSON.parse(localStorage.getItem("wvp-user"));
  18 + },
  19 +
  20 +
  21 + /**
  22 + * 获取登录token
  23 + */
  24 + getToken(){
  25 + return localStorage.getItem("wvp-token");
  26 + },
  27 +
  28 + /**
  29 + * 清理用户信息
  30 + */
  31 + clearUserInfo(){
  32 + localStorage.removeItem("wvp-user");
  33 + localStorage.removeItem("wvp-token");
  34 + },
  35 + /**
  36 + * 更新token
  37 + * @param header
  38 + */
  39 + setToken(token) {
  40 + localStorage.setItem("wvp-token", token);
  41 + }
  42 +}
web_src/src/layout/UiHeader.vue
@@ -23,9 +23,9 @@ @@ -23,9 +23,9 @@
23 <!-- </el-submenu>--> 23 <!-- </el-submenu>-->
24 <!-- <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>--> 24 <!-- <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>-->
25 <el-submenu index="" style="float: right;"> 25 <el-submenu index="" style="float: right;">
26 - <template slot="title">欢迎,{{ this.$cookies.get("session").username }}</template> 26 + <template slot="title">欢迎,{{ username }}</template>
27 <el-menu-item @click="openDoc">在线文档</el-menu-item> 27 <el-menu-item @click="openDoc">在线文档</el-menu-item>
28 - <el-menu-item > 28 + <el-menu-item>
29 <el-switch v-model="alarmNotify" inactive-text="报警信息推送" @change="alarmNotifyChannge"></el-switch> 29 <el-switch v-model="alarmNotify" inactive-text="报警信息推送" @change="alarmNotifyChannge"></el-switch>
30 </el-menu-item> 30 </el-menu-item>
31 <el-menu-item @click="changePassword">修改密码</el-menu-item> 31 <el-menu-item @click="changePassword">修改密码</el-menu-item>
@@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
39 <script> 39 <script>
40 40
41 import changePasswordDialog from '../components/dialog/changePassword.vue' 41 import changePasswordDialog from '../components/dialog/changePassword.vue'
  42 +import userService from '../components/service/UserService'
42 43
43 export default { 44 export default {
44 name: "UiHeader", 45 name: "UiHeader",
@@ -47,14 +48,17 @@ export default { @@ -47,14 +48,17 @@ export default {
47 return { 48 return {
48 alarmNotify: false, 49 alarmNotify: false,
49 sseSource: null, 50 sseSource: null,
  51 + username: userService.getUser().username,
50 activeIndex: this.$route.path, 52 activeIndex: this.$route.path,
51 - editUser: this.$cookies.get("session").roleId==1 53 + editUser: userService.getUser() ? userService.getUser().role.id === 1 : false
52 }; 54 };
53 }, 55 },
54 created() { 56 created() {
55 - console.log(this.$cookies.get("session")) 57 + console.log(4444)
  58 + console.log(JSON.stringify(userService.getUser()))
56 if (this.$route.path.startsWith("/channelList")) { 59 if (this.$route.path.startsWith("/channelList")) {
57 this.activeIndex = "/deviceList" 60 this.activeIndex = "/deviceList"
  61 +
58 } 62 }
59 }, 63 },
60 mounted() { 64 mounted() {
@@ -69,10 +73,13 @@ export default { @@ -69,10 +73,13 @@ export default {
69 method: 'get', 73 method: 'get',
70 url: "/api/user/logout" 74 url: "/api/user/logout"
71 }).then((res) => { 75 }).then((res) => {
72 - // 删除cookie,回到登录页面  
73 - this.$cookies.remove("session"); 76 + // 删除用户信息,回到登录页面
  77 + userService.clearUserInfo()
74 this.$router.push('/login'); 78 this.$router.push('/login');
75 - this.sseSource.close(); 79 + if (this.sseSource != null) {
  80 + this.sseSource.close();
  81 + }
  82 +
76 }).catch((error) => { 83 }).catch((error) => {
77 console.error("登出失败") 84 console.error("登出失败")
78 console.error(error) 85 console.error(error)
@@ -151,16 +158,19 @@ export default { @@ -151,16 +158,19 @@ export default {
151 </script> 158 </script>
152 <style> 159 <style>
153 #UiHeader .el-switch__label { 160 #UiHeader .el-switch__label {
154 - color: white ; 161 + color: white;
155 } 162 }
  163 +
156 .el-menu--popup .el-menu-item .el-switch .el-switch__label { 164 .el-menu--popup .el-menu-item .el-switch .el-switch__label {
157 color: white !important; 165 color: white !important;
158 } 166 }
159 -#UiHeader .el-switch__label.is-active{ 167 +
  168 +#UiHeader .el-switch__label.is-active {
160 color: #409EFF; 169 color: #409EFF;
161 } 170 }
  171 +
162 #UiHeader .el-menu-item.is-active { 172 #UiHeader .el-menu-item.is-active {
163 - color: #fff!important;  
164 - background-color: #1890ff!important; 173 + color: #fff !important;
  174 + background-color: #1890ff !important;
165 } 175 }
166 </style> 176 </style>
web_src/src/main.js
1 import Vue from 'vue'; 1 import Vue from 'vue';
2 import App from './App.vue'; 2 import App from './App.vue';
  3 +
3 Vue.config.productionTip = false; 4 Vue.config.productionTip = false;
4 import ElementUI from 'element-ui'; 5 import ElementUI from 'element-ui';
5 import 'element-ui/lib/theme-chalk/index.css'; 6 import 'element-ui/lib/theme-chalk/index.css';
@@ -10,15 +11,16 @@ import echarts from &#39;echarts&#39;; @@ -10,15 +11,16 @@ import echarts from &#39;echarts&#39;;
10 import VCharts from 'v-charts'; 11 import VCharts from 'v-charts';
11 12
12 import VueClipboard from 'vue-clipboard2'; 13 import VueClipboard from 'vue-clipboard2';
13 -import { Notification } from 'element-ui'; 14 +import {Notification} from 'element-ui';
14 import Fingerprint2 from 'fingerprintjs2'; 15 import Fingerprint2 from 'fingerprintjs2';
15 import VueClipboards from 'vue-clipboards'; 16 import VueClipboards from 'vue-clipboards';
16 import Contextmenu from "vue-contextmenujs" 17 import Contextmenu from "vue-contextmenujs"
  18 +import userService from "./components/service/UserService"
17 19
18 20
19 // 生成唯一ID 21 // 生成唯一ID
20 -Fingerprint2.get(function(components) {  
21 - const values = components.map(function(component,index) { 22 +Fingerprint2.get(function (components) {
  23 + const values = components.map(function (component, index) {
22 if (index === 0) { //把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样 24 if (index === 0) { //把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样
23 return component.value.replace(/\bNetType\/\w+\b/, ''); 25 return component.value.replace(/\bNetType\/\w+\b/, '');
24 } 26 }
@@ -42,13 +44,17 @@ Vue.prototype.$notify = Notification; @@ -42,13 +44,17 @@ Vue.prototype.$notify = Notification;
42 Vue.use(Contextmenu); 44 Vue.use(Contextmenu);
43 Vue.use(VCharts); 45 Vue.use(VCharts);
44 46
45 -axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : (window.baseUrl?window.baseUrl:""); 47 +axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : (window.baseUrl ? window.baseUrl : "");
46 axios.defaults.withCredentials = true; 48 axios.defaults.withCredentials = true;
47 // api 返回401自动回登陆页面 49 // api 返回401自动回登陆页面
48 -axios.interceptors.response.use(function (response) { 50 +axios.interceptors.response.use((response) => {
49 // 对响应数据做点什么 51 // 对响应数据做点什么
  52 + let token = response.headers["access-token"];
  53 + if (token) {
  54 + userService.setToken(token)
  55 + }
50 return response; 56 return response;
51 -}, function (error) { 57 +}, (error) => {
52 // 对响应错误做点什么 58 // 对响应错误做点什么
53 if (error.response.status === 401) { 59 if (error.response.status === 401) {
54 console.log("Received 401 Response") 60 console.log("Received 401 Response")
@@ -56,10 +62,22 @@ axios.interceptors.response.use(function (response) { @@ -56,10 +62,22 @@ axios.interceptors.response.use(function (response) {
56 } 62 }
57 return Promise.reject(error); 63 return Promise.reject(error);
58 }); 64 });
  65 +axios.interceptors.request.use(
  66 + config => {
  67 + if (userService.getToken() != null && config.url !== "/api/user/login") {
  68 + config.headers['access-token'] = `${userService.getToken()}`;
  69 + }
  70 + return config;
  71 + },
  72 + error => {
  73 + return Promise.reject(error);
  74 + }
  75 +);
  76 +
59 Vue.prototype.$axios = axios; 77 Vue.prototype.$axios = axios;
60 Vue.prototype.$cookies.config(60*30); 78 Vue.prototype.$cookies.config(60*30);
61 79
62 new Vue({ 80 new Vue({
63 - router: router,  
64 - render: h => h(App), 81 + router: router,
  82 + render: h => h(App),
65 }).$mount('#app') 83 }).$mount('#app')