VUE+JWT实现权限管理

最近项目要重构权限这一块,后台使用JWT token存在redis中JWT就不细说了(也可以使用shiro可自行选择),详情可百度,主要说一下实现

1.创建表resourcr(资源表,前台菜单显示的信息),user(用户表,用户的个人信息),role(角色表),user_role(用户与角色对应表),role_resource(角色资源对应表存的都是表主键id)

主要思想:创建用户,给用户分配角色,角色加载对应的权限(菜单的url)再用户登录的时候,根据角色查找资源,根据资源找到权限,最后返回tree树型结构,前台加载菜单显示

2.后台生成tree

根据需求自行创建实体类
package cn.rocketai.saas.domain.vo;

import java.util.List;

/** Author: dengWenBo Date: 19-2-20 */
public class MenuNode {
  private String id;
  private String label;
  private String pid;
  private String title;
  private String level;
  private String icon;
  private String url;
  private boolean expand = true; // 节点是否展开 默认true
  private boolean checked = false; // 选中显示勾选框
  private boolean selected = false; // 选中显示勾选背景
  private List<MenuNode> children = null;

  public MenuNode() {
    super();
  }

  public MenuNode(
      String id,
      String label,
      String pid,
      String title,
      String level,
      String icon,
      String url,
      boolean expand,
      boolean checked,
      boolean selected) {
    super();
    this.id = id;
    this.label = label;
    this.pid = pid;
    this.title = title;
    this.level = level;
    this.icon = icon;
    this.url = url;
    this.expand = expand;
    this.checked = checked;
    this.selected = selected;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getLabel() {
    return label;
  }

  public void setLabel(String label) {
    this.label = label;
  }

  public String getPid() {
    return pid;
  }

  public void setPid(String pid) {
    this.pid = pid;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public List<MenuNode> getChildren() {
    return children;
  }

  public void setChildren(List<MenuNode> children) {
    this.children = children;
  }

  public boolean isExpand() {
    return expand;
  }

  public void setExpand(boolean expand) {
    this.expand = expand;
  }

  public boolean isChecked() {
    return checked;
  }

  public void setChecked(boolean checked) {
    this.checked = checked;
  }

  public boolean isSelected() {
    return selected;
  }

  public void setSelected(boolean selected) {
    this.selected = selected;
  }

  public String getLevel() {
    return level;
  }

  public void setLevel(String level) {
    this.level = level;
  }

  public String getIcon() {
    return icon;
  }

  public void setIcon(String icon) {
    this.icon = icon;
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }
}

主要实现类结果返回tree结构

@Override
  public Map<String, Object> treeNode( List<Resources> resources) {
    Map<String, Object> map = new HashMap<>();
    List<MenuNode> menuNodeList = Lists.newArrayList();
    for (Resources res :resources ) {
      MenuNode node = new MenuNode();
      node.setId(String.valueOf(res.getId()));
      node.setPid(res.getPid());
      node.setLabel(res.getResourceName());
      node.setTitle(res.getResourceName());
      node.setLevel(String.valueOf(res.getLevel()));
      node.setIcon(res.getIcon());
      node.setUrl(res.getUrl());
      menuNodeList.add(node);
    }
    List<MenuNode> menuNodeTree = Lists.newArrayList();
    for (MenuNode node : menuNodeList) {
      if (StringUtils.equals(node.getPid(), "0")) {
        menuNodeTree.add(node);
      }
      // 找子节点
      for (MenuNode modeTree : menuNodeList) {
        if (StringUtils.equals(modeTree.getPid(), node.getId())) {
          if (node.getChildren() == null) {
            node.setChildren(new ArrayList<>());
          }
          node.getChildren().add(modeTree);
        }
      }
    }
    map.put("data", menuNodeTree);
    return map;
  }

3.前台接收数据,vue生成无限菜单

创建sideMenuItem.vue 子主键

<template>
    <Submenu :name="parentItem.title">
        <template slot="title">
            <Icon :type="parentItem.icon" :size="15"/>
            <span style="font-size: 15px">{{ parentItem.title }}</span>
        </template>
        <template v-for="item in parentItem.children">
            <side-menu-item
                    v-if="item.children&&item.children.length!==0"
                    :parent-item="item"
                    :key="'menu-'+item.id"
            >
            </side-menu-item>
            <menu-item v-else :name="item.url" :key="'menu-'+item.id">
               <!-- <Icon :type="item.icon" :size="15" />-->
                <span>{{ item.title }}</span>
            </menu-item>
        </template>
    </Submenu>
</template>

<script>
    export default {
        name: 'sideMenuItem',
        props: {
            parentItem: {
                type: Object,
                default: () => {}
            }
        }
    }
</script>

<style lang="less" scoped>

</style>

父主键 sideMenuItem.vue

<template>
    <div class="sideMenu">
        <Menu width="auto"
              :active-name="activeName"
              :open-names="openNames"
              @on-select="handleSelect"
        >
            <template v-for="item in itemList">
                <side-menu-item
                        v-if="item.children&&item.children.length!==0"
                        :parent-item="item"
                        :key="'menu-'+item.id"
                >
                </side-menu-item>
                <menu-item v-else
                           :name="item.url"
                           :key="'menu-'+item.id"
                >
                    <Icon :type="item.icon" :size="15"/>
                    <span>{{ item.title }}</span>
                </menu-item>
            </template>
        </Menu>
    </div>
</template>

<script>
    import sideMenuItem from '../components/sideMenuItem'
    import Bus from '../components/bus.js'

    export default {
        name: 'pageFooter',
        data() {
            return {
         
             
                roleIds: window.localStorage.getItem('roleIds'),
                activeName: '',//选中那个菜单
                openNames: [''], //打开选中菜子项
                itemList: [],
                changeUrl: ''
            }
        },
        created() {
          this.getResource();
            this.getChangeUrl();
        },
        methods: {
         getResource(){
          this.$ajax({
                    method: 'post',
                    url: 你自己获取treeList接口地址,
                    params: data
                }).then(response => {
               
                    var res = response.data;
                    if (res && res.code === '0') {
   
                        this.itemList  = res.data.data;
                        }  else {
                        this.$Message.error(res.msg);
                    }
                }, error => {
                    this.loading = false;

                    if (error.response) {
                        const _result = error.response.data;

                        if (_result && _result.msg) {
                            this.$Message.error(_result.msg);
                        } else {
                            this.$Message.error('网络异常,请稍后再试!');
                        }
                    }
                })
         }
            handleSelect(url) {
                this.$emit('on-select', url);
                this.$router.push({
                    path: url
                })
            },
            getChangeUrl() {
                let self = this;
                Bus.$on('change', (e) => {
                    self.activeName = e;
                })
            },
        },
        components: {
            sideMenuItem,
            Bus
        },
    }
</script>

<style lang="less" scoped>
    .sideMenu {
        padding: 30px 0 0 0;
        width: 160px;
        background-color: #fff;
        box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1);

    }
    .ivu-menu-item {
        height: 60px;
    }

</style>

最总显示结果
在这里插入图片描述

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot是一个基于Spring框架的快速开发Web应用程序的框架,Spring Security是Spring框架的安全模块,JWT是一种用于身份验证的开放标准。Vue是一种流行的JavaScript框架,用于构建用户界面。 结合这些技术,可以实现前后端分离的登录、权限管理Token管理。具体步骤如下: 1. 在Spring Boot项目中导入Spring Security和JWT的Maven依赖。 2. 配置Spring Security,包括创建用户、角色和权限等。 3. 创建一个JWT工具类,用于生成和解析Token。 4. 创建一个登录接口,接收用户名和密码,验证用户信息,生成Token并返回给前端。 5. 创建一个Token验证过滤器,用于验证请求中的Token是否有效。 6. 在Vue项目中使用Axios发送登录请求,获取Token并保存到本地存储中。 7. 在Vue项目中使用Vue Router和VueX进行路由和状态管理。 8. 创建一个路由守卫,用于验证用户是否登录和是否有权限访问某些页面。 9. 在需要进行身份验证的请求中添加Token。 下面是一个简单的示例代码,仅供参考: 后端代码: ```java // 配置Spring Security @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } } // 创建一个JWT工具类 public class JwtUtils { private static final String SECRET_KEY = "mySecretKey"; private static final long EXPIRATION_TIME = 86400000; // 24 hours public static String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject(); } public static boolean validateToken(String token, UserDetails userDetails) { String username = getUsernameFromToken(token); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } private static boolean isTokenExpired(String token) { Date expiration = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration(); return expiration.before(new Date()); } } // 创建一个登录接口 @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @PostMapping("/login") public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername()); String token = JwtUtils.generateToken(userDetails); return ResponseEntity.ok(new JwtAuthenticationResponse(token)); } } // 创建一个Token验证过滤器 public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtUtils jwtUtils; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String header = request.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { String token = header.substring(7); String username = jwtUtils.getUsernameFromToken(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtUtils.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } } // 创建一个自定义的AuthenticationEntryPoint @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } } // 创建一个自定义的UserDetailsService @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found with username: " + username); } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>()); } } // 创建一个实体类User和一个接口UserRepository @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "username") private String username; @Column(name = "password") private String password; // getters and setters } @Repository public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); } ``` 前端代码: ```javascript // 在Vue项目中使用Axios发送登录请求 axios.post('/api/auth/login', { username: 'admin', password: 'password' }).then(response => { localStorage.setItem('token', response.data.token); }); // 在需要进行身份验证的请求中添加Token axios.get('/api/users', { headers: { Authorization: 'Bearer ' + localStorage.getItem('token') } }); // 创建一个路由守卫 router.beforeEach((to, from, next) => { const publicPages = ['/login', '/register']; const authRequired = !publicPages.includes(to.path); const loggedIn = localStorage.getItem('token'); if (authRequired && !loggedIn) { return next('/login'); } next(); }); // 使用VueX进行状态管理 const store = new Vuex.Store({ state: { isLoggedIn: !!localStorage.getItem('token') }, mutations: { login(state) { state.isLoggedIn = true; }, logout(state) { state.isLoggedIn = false; } }, actions: { login({ commit }) { return new Promise(resolve => { axios.post('/api/auth/login', { username: 'admin', password: 'password' }).then(response => { localStorage.setItem('token', response.data.token); commit('login'); resolve(); }); }); }, logout({ commit }) { return new Promise(resolve => { localStorage.removeItem('token'); commit('logout'); resolve(); }); } } }); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值