1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lzq2012-vue-admin-template

В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
README.md 71 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 09.06.2025 04:46 ee13bc8

Интеграция Spring Boot Security JWT с vue-admin-template

Спасибо PanJiaChen за шаблон vue-admin-template. Я являюсь Java-разработчиком, поэтому мои навыки в фронтенде не очень сильны. Vue.js я использую только на базовом уровне, поэтому очень благодарен PanJiaChen за предоставленный шаблон.

Интегрированный шаблон доступен по адресу: https://github.com/thousmile/spring-admin-vue

Если вам понравится этот проект, пожалуйста, оставьте звезды на моем GitHub. Большое спасибо!

Если у вас возникнут вопросы при сборке, пожалуйста, обращайтесь ко мне.

GitHub: https://github.com/thousmile

Gitee: https://gitee.com/thousmile

QQ: 932560435

Начнем с базы данных

Большинство систем управления правами имеют структуру из пяти таблиц (также мы используем этот подход здесь).

t_sys_user -----> t_sys_user_role -----> t_sys_role -----> t_sys_role_permission ----> t_sys_permission

В данном случае мы будем рассматривать таблицу с правами (t_sys_permission).

Самыми важными являются поля resources и type, которые будут использоваться при интеграции с vue.js.

Поле type имеет только два типа: кнопка или меню.

Начнем сборку проекта Spring Boot. Основные зависимости в файле POM.```java

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.2</version>
    </dependency>

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.5</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

        <!-- ————————————— безопасность начало————————————————————— -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

    </dependencies>
```

### Затем добавьте конфигурацию jwt в файл application.yml
```
jwt: 
  header: jwtHeader   #заголовок JWT
  secret: eyJleHAiOjE1NDMyMDUyODUsInN1YiI6ImFkbWluIiwiY3Jl   #строка шифрования JWT
  expiration: 3600000   #время жизни JWT токена (миллисекунды)
  маршрут:
    вход: /auth/login    #адрес входа
    обновление: /auth/refresh  #адрес обновления токена
    регистрация: /auth/register  #адрес регистрации
```

### **Класс JwtUser реализует интерфейс UserDetails, определенный в spring security**

```
package com.ifsaid.admin.common.jwt;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * @author: Wang Chen Chen
 * @Date: 2018/10/29 14:08
 * @describe:
 * @version: 1.0
 */
public class JwtUser implements UserDetails {

    private String username;

    private String password;

    private Integer state;

    private Collection<? extends GrantedAuthority> authorities;
```

```java
public class JwtUser {

    public JwtUser() {
    }

    public JwtUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities) {
        this.username = username;
        this.password = password;
        this.state = state;
        this.authorities = authorities;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @JsonIgnore
    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return state == 1;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}
```

### **Класс JwtTokenUtil для работы с токенами**```java
package com.ifsaid.admin.common.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: Wang Chen Chen
 * @Date: tworocznia/10/29 14:10
 * @describe:
 * @version: 1.0
 */

@Data
@ConfigurationProperties(prefix = "jwt")
@Component
public class JwtTokenUtil implements Serializable {

    private String secret;

    private Long expiration;

    private String header;

    /**
     * Создание токена из данных
     *
     * @param claims данные
     * @return токен
     */
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
    }

    /**
     * Получение данных из токена
     *
     * @param token токен
     * @return данные
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     * Генерация токена
     *
     * @param userDetails пользователь
     * @return токен
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("sub", userDetails.getUsername());
        claims.put("created", new Date());
        return generateToken(claims);
    }
}
```    /**
     * Получение имени пользователя из токена
     *
     * @param token токен
     * @return имя пользователя
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }```markdown
### Создание拦截器 JwtAuthenticationTokenFilter для аутентификации JWT

```java
package com.ifsaid.admin.common.jwt;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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;
```

### Создание фильтра JwtAuthenticationTokenFilter для аутентификации JWT

```java
package com.ifsaid.admin.common.jwt;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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;
```/**
 * @author: Wang Chen Chen
 * @Date: 2018/10/29 14:29
 * @description:
 * @version: 1.0
 */

@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

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

        // Здесь начинается получение jwt токена из запроса
        String authHeader = request.getHeader(jwtTokenUtil.getHeader());
        log.info("authHeader:{}", authHeader);
        // Проверка наличия токена
        if (authHeader != null && StringUtils.isNotEmpty(authHeader)) {
            // Получение имени пользователя из токена
            String username = jwtTokenUtil.getUsernameFromToken(authHeader);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                // Получение информации о пользователе по имени
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

                // Проверка соответствия токена и пользователя
                if (jwtTokenUtil.validateToken(authHeader, userDetails)) {
                    // Создание объекта UsernamePasswordAuthenticationToken
                    // Затем привязка к текущему запросу, чтобы в последующих запросах можно было получить информацию о пользователе
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        chain.doFilter(request, response);
    }
}

```

### **Реализация интерфейса UserDetailsService из Spring Security**```java
package com.ifsaid.admin.service.impl;
``````markdown
Импорты:
```java
import com.ifsaid.admin.common.jwt.JwtUser;
import com.ifsaid.admin.entity.SysRole;
import com.ifsaid.admin.entity.SysUser;
import com.ifsaid.admin.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
``````java
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: Wang Chen Chen
 * @Date: 2018/10/29 14:15
 * @describe:
 * @version: 1.0
 */

@Slf4j
@Service
public class JwtUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Получение информации о пользователе из базы данных по имени пользователя
        SysUser sysUser = sysUserMapper.selectByUserName(username);
        if (sysUser == null || StringUtils.isEmpty(sysUser.getUid())) {
            throw new UsernameNotFoundException(String.format("'%s'. Этот пользователь не существует", username));
        } else {
            // Создание объекта JwtUser на основе информации о пользователе из базы данных
            List<SimpleGrantedAuthority> collect = sysUser.getRoles().stream().map(SysRole::getName).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            return new JwtUser(sysUser.getUsername(), sysUser.getPassword(), sysUser.getState(), collect);
        }
    }
}
```



### **Настройка Spring Security**

```java
package com.ifsaid.admin.config;
``````java
import com.ifsaid.admin.common.jwt.JwtAuthenticationTokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

```

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
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.configurers.ExpressionUrlAuthorizationConfigurer;
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.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
```

```java
/**
 * @author: Wang Chen Chen
 * @Date: 2018/10/29 11:41
 * @describe:
 * @version: 1.0
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebMvcConfig extends WebSecurityConfigurerAdapter {
```    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    // Здесь важно создать объект родительского класса, иначе при внедрении AuthenticationManager возникнет ошибка,
    // так как по умолчанию Spring Security не предоставляет его в контейнер.
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * @describe Основные настройки Spring Security
     * @date 2018/10/29
     * @author Wang Chen Chen
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // Открыть для доступа без аутентификации определенные маршруты. Например, маршруты для входа, обновления токена.
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
                .and().headers().cacheControl();
        // Внедрить jwt-фильтр, который мы создали ранее
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов с��域请求的配置
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
```

```markdown
   // Это конфигурация для поддержки запросов сПримечание: В данном контексте, текст был переведен на русский язык, сохраняя структуру и смысл оригинала.   // Шифрование пароля
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
```

### **Интерфейс для входа**

```java
package com.ifsaid.admin.service;

import com.ifsaid.admin.common.exception.UserExistsException;
import com.ifsaid.admin.common.service.IBaseService;
import com.ifsaid.admin.entity.SysUser;
import com.ifsaid.admin.vo.SysUserVo;
import org.springframework.security.core.AuthenticationException;

/**
 * <p>
 * [Управление правами доступа] Интерфейс для работы с пользователями
 * </p>
 *
 * @author wang chen chen
 * @since 2018-10-23
 */
public interface ISysUserService extends IBaseService<SysUser, String> {

    SysUser findByUsername(String username);

    /**
     * Получение детальной информации о пользователе
     * @param username
     * @return Результат операции
     */
    SysUserVo findUserInfo(String username);

    /**
     * Вход пользователя
     *
     * @param username Имя пользователя
     * @param password Пароль
     * @return Результат операции
     */
    String login(String username, String password) throws AuthenticationException;

    /**
     * Регистрация пользователя
     *
     * @param user Информация о пользователе
     * @return Результат операции
     */
    Integer register(SysUser sysUser) throws UserExistsException;

    /**
     * Обновление ключа
     *
     * @param oldToken Старый ключ
     * @return Новый ключ
     */
    String refreshToken(String oldToken);

}

```

### **Класс реализации для входа**

```java
package com.ifsaid.admin.service.impl;
``````java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
``````java
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * [权限管理] 用户表 服务实现类
 * </p>
 *
 * @author wang chen chen
 * @since 2018-10-23
 */

@Slf4j)
@Service
public class SysUserServiceImpl extends BaseServiceImpl<SysUser, String, SysUserMapper> implements ISysUserService {

    @Autowired
    private ISysRoleService sysRoleService;

    @Override
    public SysUser findByUsername(String username) throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username)) {
            throw new UsernameNotFoundException("Имя пользователя не может быть пустым!");
        }
        SysUser sysUser = baseMapper.selectByUserName(username);
        if (sysUser == null || StringUtils.isEmpty(sysUser.getUid()) || StringUtils.isEmpty(sysUser.getUsername())) {
            throw new UsernameNotFoundException("Пользователь не найден!");
        }
        log.info("SysUserServiceImpl......... {}", sysUser);
        return sysUser;
    }
}
```    @Override
    public SysUserVo findUserInfo(String username) {
        /**
         * Получение информации о пользователе
         */
        SysUser sysUser = findByUsername(username);
        /**
         * Получение всех ролей текущего пользователя
         */
        Set<SysRole> sysRoles = sysRoleService.selectByUserName(username);
        
         /**
         * Здесь моя идея заключается в том, чтобы создать список разрешений для кнопок
         * и список разрешений для меню
         * Таким образом, нам не придется выполнять сложную обработку на фронтенде
         * так как таблица разрешений представляет собой одну таблицу, и после ее обработки
         * фронтенд будет выполнять меньше работы, хотя это также можно выполнить на фронтенде
         */
        Set<ButtonVo> buttonVos = new HashSet<>();
        Set<MenuVo> menuVos = new HashSet<>();
```        sysRoles.forEach(role -> {
            log.info("роль: {}", role.getDescribe());
            role.getPermissions().forEach(permission -> {
                if (permission.getType().toLowerCase().equals("button")) {
                    /*
                    * Если разрешение является кнопкой, добавляем его в список кнопок
                    * */
                    buttonVos.add(new ButtonVo(permission.getPid(), permission.getResources(), permission.getTitle()));
                }
                if (permission.getType().toLowerCase().equals("menu")) {
                    /*
                    * Если разрешение является меню, добавляем его в список меню
                    * */
                    menuVos.add(new MenuVo(permission.getPid(), permission.getFather(), permission.getIcon(), permission.getResources(), permission.getTitle()));
                }
            });
        });        /**
        * Обратите внимание на класс TreeBuilder. Поскольку vue router представляет меню рекурсивно,
        * нам нужно соответствовать формату vue router для меню, а кнопки не требуют этого.
        */
        SysUserVo sysUserVo =
                new SysUserVo(sysUser.getUid(), sysUser.getAvatar(),
                        sysUser.getNickname(), sysUser.getUsername(),
                        sysUser.getMail(), sysUser.getAddTime(),
                        sysUser.getRoles(), buttonVos, TreeBuilder.findRoots(menuVos));
        return sysUserVo;
    }

    // Если в WebSecurityConfigurerAdapter не переопределено, здесь будет выброшено исключение о неудачной инъекции
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public String login(String username, String password) {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = authenticationManager.authenticate(upToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return jwtTokenUtil.generateToken(userDetails);
    }

    @Override
    public Integer register(SysUser sysUser) throws UserExistsException {
        String username = sysUser.getUsername();
        if (findByUsername(username) != null) {
            throw new UserExistsException(String.format("'%s' уже существует как имя пользователя", username));
        }
        String rawPassword = sysUser.getPassword();
        sysUser.setPassword(passwordEncoder.encode(rawPassword));
        sysUser.setUpTime(new Date());
        sysUser.setAddTime(new Date());
        return baseMapper.insertSelective(sysUser);
    }

    @Override
    public String refreshToken(String oldToken) {
        if (!jwtTokenUtil.isTokenExpired(oldToken)) {
            return jwtTokenUtil.refreshToken(oldToken);
        }
        return "error";
    }}

```

### **Контроллер для обновления токена**

```
package com.ifsaid.admin.controller;

import com.ifsaid.admin.service.ISysUserService;
import com.ifsaid.admin.vo.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * @author: Wang Chen Chen
 * @Date: 2018/10/29 10:49
 * @describe:
 * @version: 1.0
 */

@RestController
public class AuthController {

    @Autowired
    private ISysUserService sysUserService;

    @PostMapping(value = "${jwt.route.login}")
    public Result<String> login(@RequestBody Map<String, String> map) {
        String username = map.get("username");
        String password = map.get("password");
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            return Result.error401("Имя пользователя или пароль не могут быть пустыми!", null);
        }
        return Result.success("Успешный вход", sysUserService.login(username, password));
    }

    @PostMapping(value = "${jwt.route.refresh}")
    public Result<String> refresh(@RequestHeader("${jwt.header}") String token) {
        return Result.success("Токен успешно обновлен!", sysUserService.refreshToken(token));
    }

}

```

![](https://ifsaid.oss-cn-shenzhen.aliyuncs.com/article/20181030200933.png)

**На этом этапе мы уже получили токен.**

### **Затем используем токен для получения информации о пользователе**![](https://ifsaid.oss-cn-shenzhen.aliyuncs.com/article/20181030201140.png)
### **Формат JSON**
```
{
    "status": 200,
    "message": "успех",
    "data": {
        "uid": "3BDDD3B7B3AF4BA2A8FA0EFEB585597B",
        "avatar": "https://ifsaid-blog.oss-cn-shenzhen.aliyuncs.com/images/2018/9/28/3BDDD3B7B3AF4BA2A8FA0EFEB585597B.jpg",
        "nickname": "Системный администратор",
        "password": null,
        "username": "admin",
        "phone": null,
        "mail": "thousmile@163.com",
        "state": null,
        "addTime": 1540267742000,
        "upTime": null,
        "dept": null,
        "sysDept": null,
        "roles": [
            {
                "rid": 3,
                "describe": "Суперадминистратор",
                "name": "ROLE_ROOT",
                "state": null,
                "upTime": null,
                "addTime": null,
                "permissions": null
            },
            {
                "rid": 6,
                "describe": "Тестовый пользователь",
                "name": "ROLE_USER",
                "state": null,
                "upTime": null,
                "addTime": null,
                "permissions": null
            }
        ],
        "buttons": [
            {
                "pid": 47,
                "resources": "dept:update",
                "title": "Изменить отдел"
            },
            {
                "pid": 41,
                "resources": "role:new",
                "title": "Добавить роль"
            },
            {
                "pid": 34,
                "resources": "perm:delete",
                "title": "Удалить разрешение"
            },
            {
                "pid": 38,
                "resources": "user:delete",
                "title": "Удалить пользователя"
            },
            {
                "pid": 40,
                "resources": "user:view",
                "title": "Просмотреть пользователя"
            },
            {
                "pid": 44,
                "resources": "role:view",
                "title": "Просмотреть роль"
            },
            {
                "pid": 42,
                "resources": "role:delete",
                "title": "Удалить роль"
            }
        ]
    }
}
``````json
            {
                 "pid": 35,
                 "resources": "perm:update",
                 "title": "Изменить разрешение"
             },
             {
                 "pid": 48,
                 "resources": "dept:view",
                 "title": "Просмотреть отдел"
             },
             {
                 "pid": 37,
                 "resources": "dept:delete",
                 "title": "Удалить отдел"
             }
         ]
     }
 }
 ``````markdown
                 "resources": "user:new",
                  "title": "Добавить пользователя"
              },
              {
                  "pid": 33,
                  "resources": "perm:new",
                  "title": "Добавить разрешение"
              },
              {
                  "pid": 43,
                  "resources": "role:update",
                  "title": "Изменить роль"
              },
              {
                  "pid": 45,
                  "resources": "dept:new",
                  "title": "Добавить отдел"
              },
              {
                  "pid": 39,
                  "resources": "user:update",
                  "title": "Изменить пользователя"
              },
              {
                  "pid": 36,
                  "resources": "perm:view",
                  "title": "Просмотреть разрешение"
              },
              {
                  "pid": 46,
                  "resources": "dept:delete",
                  "title": "Удалить отдел"
              }
          ],
          "menus": [
              {
                  "pid": 2,
                  "father": 0,
                  "icon": "sys_set",
                  "resources": "sys",
                  "title": "Системные настройки",
                  "children": [
                      {
                          "pid": 51,
                          "father": 2,
                          "icon": "sys_wechat",
                          "resources": "wechat",
                          "title": "Настройки WeChat",
                          "children": null
                      },
                      {
                          "pid": 52,
                          "father": 2,
                          "icon": "sys_backstage",
                          "resources": "backstage",
                          "title": "Настройки后台",
                          "children": null
                      }
                  ]
              }
          ]
``````markdown
{
    "code": 200,
    "data": {
        "pid": 1,
        "father": 0,
        "icon": "sys_admin",
        "resources": "dept",
        "title": "Управление отделами",
        "children": [
            {
                "pid": 28,
                "father": 1,
                "icon": "dept_admin",
                "resources": "dept",
                "title": "Управление отделами",
                "children": null
            },
            {
                "pid": 30,
                "father": 1,
                "icon": "user__admin",
                "resources": "user",
                "title": "Управление пользователями",
                "children": null
            },
            {
                "pid": 31,
                "father": 1,
                "icon": "role__admin",
                "resources": "role",
                "title": "Управление ролями",
                "children": null
            },
            {
                "pid": 29,
                "father": 1,
                "icon": "setting__backend",
                "resources": "setting",
                "title": "Настройки административной панели",
                "children": null
            }
        ]
    }
}
``````json
[
    {
        "pid": 1,
        "father": 0,
        "icon": "prem_admin",
        "resources": "perm",
        "title": "Управление правами",
        "children": null
    },
    {
        "pid": 3,
        "father": 0,
        "icon": "sys_control",
        "resources": "control",
        "title": "Мониторинг системы",
        "children": [
            {
                "pid": 50,
                "father": 3,
                "icon": "control_logs",
                "resources": "logs",
                "title": "Системные журналы",
                "children": null
            },
            {
                "pid": 49,
                "father": 3,
                "icon": "control_database",
                "resources": "database",
                "title": "Мониторинг базы данных",
                "children": null
            }
        ]
    }
],
"error": null,
"timestamp": 1540901472256
]
```

**Документ разделяется на три основные части****1. Информация о пользователе**

**2. Меню (рекурсивная форма)**

**3. Список кнопок (форма списка)**

## Затем начинаем интеграцию [**vue-element-admin**](https://github.com/PanJiaChen/vue-element-admin/blob/master/README.zh-CN.md)

Сначала посмотрим на результат.

![](https://ifsaid.oss-cn-shenzhen.aliyuncs.com/article/20181030201718.png)

![](https://ifsaid.oss-cn-shenzhen.aliyuncs.com/article/20181030201808.png)

Скачиваем [**vue-element-admin**](https://github.com/PanJiaChen/vue-element-admin/blob/master/README.zh-CN.md) с GitHub.

![](https://ifsaid.oss-cn-shenzhen.aliyuncs.com/article/20181030202432.png)

```npm
# Клонируем проект
git clone https://github.com/PanJiaChen/vue-admin-template.git

# Рекомендуется не использовать cnpm, так как установка с ним имеет множество странных проблем. Можно решить проблему медленной установки npm следующим образом
npm install --registry=https://registry.npm.taobao.org

# Запуск с горячей перезагрузкой на localhost:9528
npm run dev
```

**1. Изменяем API для входа на указанный в Spring Boot**   Путь /src/api/login.js 

```js
import request from '@/utils/request'

// Вход
export function login(username, password) {
  return request({ url: '/auth/login', method: 'post', data: { username, password }})
}

// Получение информации о пользователе
export function getInfo(token) {
  return request({ url: 'user/info', method: 'get' })
}

// Выход
export function logout() {
  return request({ url: 'user/logout', method: 'post' })
}

```

### **2. Изменяем конфигурацию axios**   Путь /src/utils/request.js

```js
import axios from 'axios'
import { Message, MessageBox } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'

// Создаем экземпляр axios
const service = axios.create({
  baseURL: process.env.BASE_API, // api базовый URL
  timeout: 5000 // время ожидания запроса
})
``````markdown
// request-интерцептор
service.interceptors.request.use(
  config => {
    if (getToken() !== '') {
      config.headers['jwtHeader'] = getToken() // Добавляем токен в каждый запрос, пожалуйста, измените по необходимости
    }
    return config
  },
  error => {
    // Обрабатываем ошибку запроса
    console.log(error) // для отладки
    return Promise.reject(error)
  }
)

// response-интерцептор
service.interceptors.response.use(
  response => {
    /**
     * Если code не равен 20000, выбрасываем ошибку. Можно изменить по необходимости
     */
    const res = response.data
    if (res.status !== 200) {
      Message({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      })
      // 50008: Недействительный токен; 50012: Другой клиент вошел в систему; 50014: Токен истек;
      if (res.code === 400 || res.code === 401 || res.code === 402) {
        MessageBox.confirm('Вы были вынуждены выйти из системы. Вы можете отменить это действие, чтобы оставаться на этой странице, или войти снова.', 'Подтвердите выход',
          { confirmButtonText: 'Войти снова',
            cancelButtonText: 'Отмена',
            type: 'warning'
          }
        ).then(() => {
          store.dispatch('FedLogOut').then(() => {
            location.reload() // для пересоздания объекта vue-router, чтобы избежать ошибок
          })
        })
      }
      return Promise.reject('ошибка')
    } else {
      return response.data
    }
  },
  error => {
    console.log('ошибка ' + error) // для отладки
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

```

### **3. Измените /config/dev.env.js и /config/prod.env.js**

```javascript
// dev.env.js

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
```module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '"http://localhost:8080"',
})



// prod.env.js

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  BASE_API: '"http://localhost:8080"',
}


```

### **4. Измените vuex для установки токена при входе и получения информации о пользователе**  
**Директория /src/store/modules/user.js**

```javascript
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'

const user = {
  state: {
    token: getToken(),
    nickname: '',
    avatar: '',
    uid: '',
    user: {},
    roles: [],
    menus: [], // разрешения на меню
    buttons: [] // разрешения на кнопки
  },
``````markdown
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_INFO: (state, user) => {
      state.nickname = user.nickname
      state.avatar = user.avatar
      state.uid = user.uid
      state.user = user
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_MENUS: (state, menus) => {
      state.menus = menus
    },
    SET_BUTTONS: (state, buttons) => {
      state.buttons = buttons
    }
  },
```javascript
    actions: {
    // Вход
    Login({ commit }, userInfo) {
      const username = userInfo.username.trim()
      return new Promise((resolve, reject) => {
        login(username, userInfo.password).then(res => {
          setToken(res.data)
          commit('SET_TOKEN', res.data)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
}```javascript
    // Получение информации о пользователе
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getInfo(state.token).then(res => {
          const data = res.data
          if (data.roles && data.roles.length > 0) { // Проверка, является ли возвращенное значение roles непустым массивом
            commit('SET_ROLES', data.roles)
          } else {
            reject('getInfo: roles must be a non-null array!')
          }
          commit('SET_MENUS', data.menus)
          commit('SET_BUTTONS', data.buttons)
          // Установка информации о пользователе
          commit('SET_INFO', data)
          resolve(res)
        }).catch(error => {
          reject(error)
        })
      })
    },
``````markdown
export default new Router({
  // mode: 'history', // поддержка с серверной стороны
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRouterMap
})
```

```javascript
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Layout from '@/views/layout/Layout'
// По умолчанию, маршрутная цепочка. Маршруты, общие для всех пользователей
export const constantRouterMap = [
  {
    path: '/login',
    name: 'Login',
    component: () =>
      import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    name: 'Dashboard',
    hidden: true,
    children: [{
      path: 'dashboard',
      component: () =>
        import('@/views/dashboard/index')
    }, {
      path: 'userinfo',
      name: 'UserInfo',
      component: () =>
        import('@/views/dashboard/userinfo')
    }]
  },

  {
    path: '/error',
    component: Layout,
    redirect: '/error/404',
    hidden: true,
    children: [{
      path: '404',
      component: () =>
        import('@/views/error/404/index')
    }, {
      path: '401',
      component: () =>
        import('@/views/error/401/index')
    }]
  },
  {
    path: '*',
    redirect: '/error/404',
    hidden: true
  }
]
```

```javascript
    // Выход
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_INFO', '')
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    // Выход из системы (фронтенд)
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}

export default user
```

### **5. Изменение vue-router**  Путь /src/router/index.js

```javascript
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Layout from '@/views/layout/Layout'
// По умолчанию, маршрутная цепочка. Маршруты, общие для всех пользователей
export const constantRouterMap = [
  {
    path: '/login',
    name: 'Login',
    component: () =>
      import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    name: 'Dashboard',
    hidden: true,
    children: [{
      path: 'dashboard',
      component: () =>
        import('@/views/dashboard/index')
    }, {
      path: 'userinfo',
      name: 'UserInfo',
      component: () =>
        import('@/views/dashboard/userinfo')
    }]
  },

  {
    path: '/error',
    component: Layout,
    redirect: '/error/404',
    hidden: true,
    children: [{
      path: '404',
      component: () =>
        import('@/views/error/404/index')
    }, {
      path: '401',
      component: () =>
        import('@/views/error/401/index')
    }]
  },
  {
    path: '*',
    redirect: '/error/404',
    hidden: true
  }
]
```

```javascript
export default new Router({
  // mode: 'history', // поддержка с серверной стороны
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRouterMap
})
```// Асинхронно монтируемые маршруты
// Динамические маршруты, которые необходимо загружать в зависимости от прав доступа
// Этот маршрут является наиболее полным, соответствует записям в базе данных
// В зависимости от прав доступа пользователя, из этого маршрута извлекаются маршруты, соответствующие текущему пользователю, и добавляются в vue-router
// meta: наиболее важным атрибутом является resources, который используется для сравнения с meta.resources из полученной информации о пользователе
export const asyncRouterMap = [
  {
    path: '/pre',
    component: Layout,
    name: 'pre',
    meta: {
      resources: 'pre',
      title: 'Управление правами доступа'
    },
    children: [
      {
        path: 'index',
        component: () => import('@/views/pre/perm/index'),
        name: 'perm',
        meta: {
          resources: 'perm'
        }
      },
      {
        path: 'user',
        component: () => import('@/views/pre/user/index'),
        name: 'user',
        meta: {
          resources: 'user'
        }
      },
      {
        path: 'role',
        component: () => import('@/views/pre/role/index'),
        name: 'role',
        meta: {
          resources: 'role'
        }
      },
      {
        path: 'dept',
        component: () => import('@/views/pre/dept/index'),
        name: 'dept',
        meta: {
          resources: 'dept'
        }
      }
    ]
  }
]  {
    path: '/sys',
    component: Layout,
    name: 'sys',
    meta: {
      resources: 'sys',
      title: 'Системные настройки'
    },
    children: [
      {
        path: 'index',
        component: () => import('@/views/sys/backstage/index'),
        name: 'backstage',
        meta: {
          resources: 'backstage'
        }
      },
      {
        path: 'wechat',
        component: () => import('@/views/sys/wechat/index'),
        name: 'wechat',
        meta: {
          resources: 'wechat'
        }
      }
    ]
  },

  {
    path: 'external-link',
    component: Layout,
    name: 'Link',
    meta: {
      resources: 'control',
      title: 'Мониторинг системы',
      icon: 'link'
    },
    children: [{
      path: 'https://www.baidu.com/',
      meta: {
        resources: 'logs',
        title: 'Журнал системы',
        icon: 'link'
      }
    },
    {
      path: 'https://v.qq.com/',
      meta: {
        resources: 'database',
        title: 'Мониторинг базы данных',
        icon: 'link'
      }
    }
    ]
  }
]
```


### **6. Добавьте управление правами и добавьте в vuex /src/store/modules/permission.js

```javascript
// store/permission.js
import { asyncRouterMap, constantRouterMap } from '@/router'

/**
 *
 * @param {Array} userRouter права пользователя, полученные с сервера
 * @param {Array} allRouter все динамические маршруты, настроенные на фронте
 * @return {Array} realRoutes отфильтрованные маршруты
 */

export function recursionRouter(userRouter = [], allRouter = []) {
  var realRoutes = []
  allRouter.forEach((v, i) => {
    userRouter.forEach((item, index) => {
      if (item.resources === v.meta.resources) {
        if (item.children && item.children.length > 0) {
          v.children = recursionRouter(item.children, v.children)
        }
        v.meta.title = item.title
        v.meta.icon = item.icon
        realRoutes.push(v)
      }
    })
  })
  return realRoutes
}
``````javascript
/**
 *
 * @param {Array} routes отфильтрованные маршруты пользователя
 *
 * Рекурсивное установление первого children.path как по умолчанию для всех маршрутов с дочерними маршрутами
 */
export function setDefaultRoute(routes) {
  routes.forEach((v, i) => {
    if (v.children && v.children.length > 0) {
      v.redirect = { name: v.children[0].name };
      setDefaultRoute(v.children);
    }
  });
}
```const permission = {
  state: {
    routers: constantRouterMap, // Это список маршрутов по умолчанию, например, 404, 500 и т.д.
    dynamicRouters: [] // Это права, полученные с сервера
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.dynamicRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        // Переданный список menus используется для рекурсивного построения маршрутов и сохранения их в vuex
        commit('SET_ROUTERS', recursionRouter(data, asyncRouterMap))
        resolve()
      })
    }
  }
}

export default permission

```

### **7. Измените файл /src/permission.js, чтобы изменить условия для настройки маршрутов**```javascript
import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress прогресс-бар
import 'nprogress/nprogress.css'// Progress стили прогресс-бара
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth' // авторизация
``````markdown
const whiteList = ['/login'] // Список для исключения из перенаправления
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done() // Если текущая страница — это дашборд, то afterEach-хук не будет запущен, поэтому обрабатываем это вручную
    } else {
      if (store.getters.menus.length === 0) {
        // Получение информации о пользователе (необходимо убедиться, что в методе GetInfo уже получены списки меню)
        store.dispatch('GetInfo').then(res => {
          // Динамическое установление маршрутов (передаем полученные данные пользователя методу GenerateRoutes для анализа)
          store.dispatch('GenerateRoutes', store.getters.menus).then(r => {
            // Получаем уже сгенерированные маршруты и динамически добавляем их в router
            router.addRoutes(store.getters.dynamicRouters)
            // Хак для обеспечения завершения addRoutes
            next({ ...to, replace: true })
          })
        }).catch((err) => {
          store.dispatch('FedLogOut').then(() => {
            Message.error(err || 'Проверка не пройдена, пожалуйста, войдите снова')
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.path}`) // В противном случае перенаправляем на страницу входа
      NProgress.done()
    }
  }
})```javascript
router.afterEach(() => {
  NProgress.done() // Завершение прогресса
})

```

### **8. Изменение способа отображения меню. По умолчанию, маршруты для отображения берутся из vue-router, но сейчас они хранятся в vuex.**

Директория: /src/views/layout/components/Sidebar/index.vue

```javascript
<template>
  <el-scrollbar wrap-class="scrollbar-wrapper">

    <logo :is-collapse="isCollapse"/>

    <el-menu
      :show-timeout="200"
      :default-active="$route.path"
      :collapse="isCollapse"
      mode="vertical"
      background-color="#304156"
      text-color="#bfcbd9"
      active-text-color="#409EFF"
    >
      
   		<!-- Основное изменение здесь: menu_routers -->
      <sidebar-item v-for="route in menu_routers" :key="route.path" :item="route" :base-path="route.path"/>
    </el-menu>
  </el-scrollbar>
</template>

<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
import logo from './Logo'
</script>
```

```javascript
export default {
  components: {
    SidebarItem,
    logo
  },
  computed: {
      // Здесь получаем список меню из vuex
    ...mapGetters([
      'menu_routers',
      'sidebar'
    ]),
    isCollapse() {
      return !this.sidebar.opened
    }
  }
}
</script>

```

### **До этого момента удалось сделать динамическое меню, осталось разобраться с правами доступа к кнопкам, что довольно просто, достаточно добавить одно Vue-направление**

```javascript
import Vue from 'vue'
import store from '@/store'

/** Направление для проверки прав доступа **/

Vue.directive('has', {
  bind: function(el, binding) {
    if (!Vue.prototype.$_has(binding.value)) {
      el.parentNode.removeChild(el)
    }
  }
})

// Метод проверки прав доступа
Vue.prototype.$_has = function(value) {
  // Получаем права доступа пользователя
  let isExist = false
  const dynamicButtons = store.getters.buttons
  if (dynamicButtons === undefined || dynamicButtons === null || dynamicButtons.length < 1) {
    return isExist
  }
  dynamicButtons.forEach(button => {
    if (button.resources === value) {
      isExist = true
      return isExist
    }
  })
  return isExist
}

Тестируем направление на странице

<!-- Значение v-has — это свойство resources кнопок, полученное при получении информации о пользователе. Мы сравниваем это значение. -->

<el-button v-has="'perm:new'" class="btns">Добавить</el-button>

<el-button v-has="'perm:haha'" class="btns">Хаха</el-button>

На этом моменте был завершен процесс интеграции Spring Boot, Security, JWT, Vue-Admin-Template, создание полного проекта с разделением на фронтенд и бэкенд, динамическим управлением правами доступа до уровня кнопок, и создание системы управления сайтом

Если вам понравился этот проект, пожалуйста, оставьте звезды на моем GitHub. Большое спасибо!

Если у вас возникли проблемы при сборке проекта, пожалуйста, обращайтесь ко мне.

GitHub: https://github.com/thousmile

Gitee: https://gitee.com/thousmile

QQ: 932560435


Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/lzq2012-vue-admin-template.git
git@api.gitlife.ru:oschina-mirror/lzq2012-vue-admin-template.git
oschina-mirror
lzq2012-vue-admin-template
lzq2012-vue-admin-template
master