IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vhr学习整理 -> 正文阅读

[JavaScript知识库]vhr学习整理

vhr

vue-cli3构建vue项目

vue.js官网,安装,命令行工具,vue-cli文档,npm安装

npm install -g @vue/cli

vue --version

vue create vuehr

default

cd vuehr

npm run serve

项目结构

main.js->App.vue->router.js->login.vue

package.json工具包

安装element插件

npm i element-ui -S

引入

import ElementUI from 'element-ui';
Vue.use(ElementUI);

微人事登录页面制作 处理前端登录事件

login.vue

<template>
  <div>
    <el-form :rules="rules" ref="loginForm" v-loading="loading"
             element-loading-text="正在登录..."
             element-loading-spinner="el-icon-loading"
             element-loading-background="rgba(0, 0, 0, 0.8)" :model="loginForm" class="loginContainer">
                 
      <h3 class="LoginTitle">系统登录</h3>
      <el-form-item prop="username">
        <el-input type="text" v-model="loginForm.username" auto-complete="off" placeholder="请输入用户名"></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input size="normal" type="text" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" @keydown.enter.native="submitLogin"></el-input>
      </el-form-item>
      <el-checkbox size="normal" class="LoginRemember" v-model="checked"></el-checkbox>
      <el-button size="normal" type="primary" style="width:100%;" @click="submitLogin">登录</el-button>
    </el-form>
  </div>
</template>

<script>

  export default {
    name: "Login",
    data(){
      return{
        loading:false,
        checked:true,
        loginForm:{
          username:'admin',
          password: '123'
        },
        rules:{
          username:[{required:true,message:'请输入用户名',trigger:'blur'}],
          password:[{required:true,message:'请输入密码',trigger:'blur'}]
        }
      }
    },
    methods:{
      submitLogin() {

        this.$refs.loginForm.validate((valid) => {
          if (valid) {
            this.loading = true;
            // resp服务端返回的json
            this.postKeyValueRequest('/doLogin',this.loginForm).then(resp=>
            {
              this.loading=false;
              if(resp){
                //用户登陆成功后的数据将被保存再sessionStorage中,防止用户刷新后数据丢失,
                //  以字符串形式存入,取的时候转为json
                window.sessionStorage.setItem("user",JSON.stringify(resp));
                //跳转后能否到重定向页面中
                let path=this.$route.query.redirect;

                // 登录成功后进行页面跳转
                this.$router.replace((path==='/'||path===undefined)?'/home':path)
              }
            })
          } else {
            this.$message.error("请输入所有字段");
            return false;
          }
        });
      }
    }
  }
</script>

<style>
  .loginContainer{
    border-radius:15px;
    background-clip: padding-box;
    margin: 180px auto;
    width:350px;
    padding:15px 35px 15px 35px;
    background: #f6f6f6;
    border:1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
  }
  .LoginTitle{
    margin: 15px auto 20px auto;
    text-align: center;
  }
  .LoginRemember{
    text-align: left;
    margin:0px 0px 15px 0;
  }

</style>

home页页面跳转

<div>
    home页
</div>

服务端环境搭建

创建vhr数据库,导入表

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.javagirl</groupId>
    <artifactId>vhr_project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>vhr_project</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.3.9.RELEASE</version>
        </dependency>
        <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>2.1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
            <scope>runtime</scope>
        </dependency>
<!--        数据库驱动-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>

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

            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-devtools</artifactId>-->
<!--            <optional>true</optional>-->
<!--        </dependency>-->

    </dependencies>

    <build>
<!--        maven文件配置识别mapper的xml文件-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
        <plugins>
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
<!--            <plugin>-->
<!--                <groupId>org.apache.maven.plugins</groupId>-->
<!--                <artifactId>maven-surefire-plugin</artifactId>-->
<!--                <configuration>-->
<!--                    <skipTests>true</skipTests>-->
<!--                </configuration>-->
<!--            </plugin>-->
        </plugins>
    </build>

</project>

使用逆向工程生成mapper和model文件

主运行文件配置mapperScan

@SpringBootApplication
@MapperScan(basePackages = "org.javagirl.vhr_project.mapper")
public class VhrProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(VhrProjectApplication.class, args);
    }

}

配置application文件

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3307/vhr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
server.port=8085

服务端登录接口制作

model中的hr

package org.javagirl.vhr_project.model;

/**
 * Hr类,实现UserDetails接口的方法
 * 不涉及到账户的锁定、密码的过期等等,只有账户是否被禁用,因此只处理了isEnabled方法
 *
 * */

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Hr implements UserDetails {
    private Integer id;

    private String name;

    private String phone;

    private String telephone;

    private String address;

    private Boolean enabled;

    private String username;

    private String password;

    private String userface;

    public Boolean getEnabled() {
        return enabled;
    }



    private String remark;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    private List<Role> roles;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone == null ? null : phone.trim();
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone == null ? null : telephone.trim();
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address == null ? null : address.trim();
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public String getUsername() {
        return username;
    }

//    账户是否过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

//    账户是否被锁定
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

//    密码是否过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

//    账户是否允许被使用
    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }

    //获取当前用户所具有的角色
    @Override
    //生成json时忽略该属性
    @JsonIgnore
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
        //roles属性描述当前用户的角色
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

    public String getUserface() {
        return userface;
    }

    public void setUserface(String userface) {
        this.userface = userface == null ? null : userface.trim();
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark == null ? null : remark.trim();
    }
}

HrService

package org.javagirl.vhr_project.service;

import org.javagirl.vhr_project.mapper.HrMapper;
import org.javagirl.vhr_project.mapper.HrRoleMapper;
import org.javagirl.vhr_project.model.Hr;
import org.javagirl.vhr_project.utils.HrUtils;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * HrService实现UserDetailsService接口
 * 1.根据用户名查找用户
 * 2.查找、更新、删除hr
 * 3.更新hr角色
 */
@Service
public class HrService implements UserDetailsService {
    @Autowired
    HrMapper hrMapper;
    @Autowired
    HrRoleMapper hrRoleMapper;

    //在执行登陆的过程中,这个方法根据用户名取查找用户
    //如果用户不存在,抛出异常,否则返回Hr
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Hr hr = hrMapper.loadUserByUsername(username);
        if(hr == null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        hr.setRoles(hrMapper.getHrRolesById(hr.getId()));
        return hr;

    }

    /**
     * 查找所有Hr
     * @param keywords
     * @return List
     */
    public List<Hr> getAllHrs(String keywords) {
        return hrMapper.getAllHrs(HrUtils.getCurrentHr().getId(),keywords);
    }

    /**
     * 更新hr
     * @param hr
     * @return hr.id
     */
    public Integer updateHr(Hr hr) {
        return hrMapper.updateByPrimaryKeySelective(hr);
    }

    /**
     * 更新hr的角色
     * @param hrid
     * @param rids
     * @return boolean
     */
    @Transactional
    public boolean updateHrRole(Integer hrid, Integer[] rids) {
        //先删除hr的角色
        hrRoleMapper.deleteByHrid(hrid);
        //再给hr添加角色
        return hrRoleMapper.addRole(hrid,rids)==rids.length;

    }

    /**
     * 删除hr
     * @param id
     * @return int
     */
    public Integer deleteHrById(Integer id) {
        return hrMapper.deleteByPrimaryKey(id);
    }
}

HrMapper接口

package org.javagirl.vhr_project.mapper;

import org.apache.ibatis.annotations.Param;
import org.javagirl.vhr_project.model.Hr;
import org.javagirl.vhr_project.model.Role;

import java.util.List;

public interface HrMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Hr record);

    int insertSelective(Hr record);

    Hr selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Hr record);

    int updateByPrimaryKey(Hr record);

    Hr loadUserByUsername(String username);

    List<Role> getHrRolesById(Integer id);

    List<Hr> getAllHrs(@Param("hrid") Integer hrid,String keywords);
}

HrMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.javagirl.vhr_project.mapper.HrMapper" >
  <resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Hr" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="phone" property="phone" jdbcType="CHAR" />
    <result column="telephone" property="telephone" jdbcType="VARCHAR" />
    <result column="address" property="address" jdbcType="VARCHAR" />
    <result column="enabled" property="enabled" jdbcType="BIT" />
    <result column="username" property="username" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="userface" property="userface" jdbcType="VARCHAR" />
    <result column="remark" property="remark" jdbcType="VARCHAR" />
  </resultMap>
  <resultMap id="HrWithRoles" type="org.javagirl.vhr_project.model.Hr" extends="BaseResultMap">
    <collection property="roles" ofType="org.javagirl.vhr_project.model.Role">
      <id column="rid" property="id"/>
      <result column="rname" property="name"/>
      <result column="rnameZh" property="nameZh"></result>
    </collection>
  </resultMap>
  <sql id="Base_Column_List" >
    id, name, phone, telephone, address, enabled, username, password, userface, remark
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from hr
    where id = #{id,jdbcType=INTEGER}
  </select>
  <select id="loadUserByUsername" resultMap="BaseResultMap"
          >
    select * from hr where username=#{username}
  </select>
  <select id="getHrRolesById" resultType="org.javagirl.vhr_project.model.Role"
          parameterType="java.lang.Integer">
    SELECT r.* FROM role r,hr_role hrr WHERE hrr.`rid`=r.`id` AND hrr.`hrid`=#{id}
  </select>
    <select id="getAllHrs" resultMap="HrWithRoles">
      SELECT hr.id, hr.name, hr.phone, hr.telephone, hr.address, hr.enabled, hr.username, hr.userface, hr.remark,r.`id` AS rid,r.name AS rname,r.`nameZh` AS rnameZh FROM hr LEFT JOIN hr_role hrr ON hr.`id`=hrr.hrid LEFT JOIN role r ON hrr.`rid`=r.`id` WHERE hr.id!=#{hrid} <if test="keywords!=null">and hr.name like concat('%',#{keywords},'%')</if> order by hr.id
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from hr
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="org.javagirl.vhr_project.model.Hr" >
    insert into hr (id, name, phone, 
      telephone, address, enabled, 
      username, password, userface, 
      remark)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{phone,jdbcType=CHAR}, 
      #{telephone,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{enabled,jdbcType=BIT}, 
      #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{userface,jdbcType=VARCHAR}, 
      #{remark,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="org.javagirl.vhr_project.model.Hr" >
    insert into hr
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="name != null" >
        name,
      </if>
      <if test="phone != null" >
        phone,
      </if>
      <if test="telephone != null" >
        telephone,
      </if>
      <if test="address != null" >
        address,
      </if>
      <if test="enabled != null" >
        enabled,
      </if>
      <if test="username != null" >
        username,
      </if>
      <if test="password != null" >
        password,
      </if>
      <if test="userface != null" >
        userface,
      </if>
      <if test="remark != null" >
        remark,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null" >
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null" >
        #{phone,jdbcType=CHAR},
      </if>
      <if test="telephone != null" >
        #{telephone,jdbcType=VARCHAR},
      </if>
      <if test="address != null" >
        #{address,jdbcType=VARCHAR},
      </if>
      <if test="enabled != null" >
        #{enabled,jdbcType=BIT},
      </if>
      <if test="username != null" >
        #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        #{password,jdbcType=VARCHAR},
      </if>
      <if test="userface != null" >
        #{userface,jdbcType=VARCHAR},
      </if>
      <if test="remark != null" >
        #{remark,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="org.javagirl.vhr_project.model.Hr" >
    update hr
    <set >
      <if test="name != null" >
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null" >
        phone = #{phone,jdbcType=CHAR},
      </if>
      <if test="telephone != null" >
        telephone = #{telephone,jdbcType=VARCHAR},
      </if>
      <if test="address != null" >
        address = #{address,jdbcType=VARCHAR},
      </if>
      <if test="enabled != null" >
        enabled = #{enabled,jdbcType=BIT},
      </if>
      <if test="username != null" >
        username = #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        password = #{password,jdbcType=VARCHAR},
      </if>
      <if test="userface != null" >
        userface = #{userface,jdbcType=VARCHAR},
      </if>
      <if test="remark != null" >
        remark = #{remark,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="org.javagirl.vhr_project.model.Hr" >
    update hr
    set name = #{name,jdbcType=VARCHAR},
      phone = #{phone,jdbcType=CHAR},
      telephone = #{telephone,jdbcType=VARCHAR},
      address = #{address,jdbcType=VARCHAR},
      enabled = #{enabled,jdbcType=BIT},
      username = #{username,jdbcType=VARCHAR},
      password = #{password,jdbcType=VARCHAR},
      userface = #{userface,jdbcType=VARCHAR},
      remark = #{remark,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

securityConfig

respBean

package org.javagirl.vhr_project.model;

/**
 * 返回数据封装
 */
public class RespBean {
    private RespBean(Integer status, String msg, Object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }
    private RespBean() {
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    private Integer status;
    private String msg;
    private Object obj;
//    返回json格式文件



    public static RespBean ok(String msg){
        return new RespBean(200,msg,null);
    }
    public static RespBean ok(String msg,Object obj){
        return new RespBean(200,msg,obj);
    }
    public static RespBean error(String msg){
        return new RespBean(500,msg,null);
    }
    public static RespBean error(String msg,Object obj){
        return new RespBean(500,msg,obj);
    }



}

ctrl+H查看继承类

package org.javagirl.vhr_project.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.javagirl.vhr_project.model.Hr;
import org.javagirl.vhr_project.model.RespBean;
import org.javagirl.vhr_project.service.HrService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

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

/**
 * 对登录页面进行处理
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    HrService hrService;
    @Autowired
    CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;
    @Autowired
    CustomUrlDecisionManager customUrlDecisionManager;

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    //加载hrservice安全管理
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(hrService);
    }


//    对登录页面进行放权
    @Override
    public void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/login","/css/**","/js/**","/index.html","/img/**","/fonts/**","favicon.ico");
        web.ignoring().antMatchers("/login");
    }

    //通过withObjectPostProcessor将刚刚创建的CustomFilterInvocationSecurityMetadataSource和CustomUrlDecisionManager注入。请求都会经过刚才的过滤器(除了configure(WebSecurity web)方法忽略的请求)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
//                .anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/doLogin")
                //重新配置登录页
                .loginPage("/login")
//                登录成功,配置登录成功时返回的JSON,登录成功时返回当前用户的信息
                .successHandler(new AuthenticationSuccessHandler() {
                    //登录成功的回调
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out=resp.getWriter();
                        Hr hr=(Hr)authentication.getPrincipal();
                        hr.setPassword(null);
                        RespBean ok = RespBean.ok("登陆成功", hr);
                        String s = new ObjectMapper().writeValueAsString(hr);
                        out.write(s);
                        out.flush();
                        out.close();
                    }
                })
//                登录失败,表示登录失败,根据不同的异常输出不同的错误提示
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out=resp.getWriter();
                        RespBean respBean = RespBean.error("登陆失败");
//                        查看异常的父类 ctrl H
                        if(exception instanceof LockedException){
                            respBean.setMsg("账户被锁定");
                       }else if(exception instanceof CredentialsExpiredException){
                            respBean.setMsg("密码过期");
                        }else if(exception instanceof AccountExpiredException){
                            respBean.setMsg("账户过期");
                        }else if(exception instanceof DisabledException){
                            respBean.setMsg("账户被禁用");
                        }else if(exception instanceof BadCredentialsException){
                            respBean.setMsg("用户名或者密码输入错误,请重新输入");
                        }
                        out.write(new ObjectMapper().writeValueAsString(respBean));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .logout()
//                注销登录
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功")));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                //开启测试
                .csrf().disable().exceptionHandling()
//                没有认证时,在这里处理结果,不要重定向
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {
                resp.setContentType("application/json;charset=utf-8");
                resp.setStatus(401);
                PrintWriter out=resp.getWriter();
                RespBean respBean = RespBean.error("访问失败");
//                        查看异常的父类 ctrl H
                if(authException instanceof InsufficientAuthenticationException){
                    respBean.setMsg("请求失败,请联系管理员");
                }
                out.write(new ObjectMapper().writeValueAsString(respBean));
                out.flush();
                out.close();
            }
        });
    }
}
 
package org.javagirl.vhr_project.controller;

import org.javagirl.vhr_project.model.RespBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 配置登录页面
 */
@RestController
public class LoginController {
    @GetMapping("/login")
    public RespBean login(){
        return RespBean.error("尚未登陆,请登陆");
    }
}

/doLogin为登录接口,自己定义一个helloController接口进行测试

前端请求方法封装

npm install axios

api.js

import axios from 'axios'
import {Message} from "element-ui";
import router from '../router'

/**
 * 响应拦截器
 * 采用axios处理网络请求,避免在每次请求时判断各种网络情况(连接超时,服务器内部错误,权限不足等),对axios进行简单的封装,使用axios的拦截器功能
 */
axios.interceptors.response.use(success=>{
    // 展示错误信息
    if(success.status && success.status===200 &&success.data.status===500){
        //业务错误
        Message.error({message:success.data.msg})
        //返回空
        return;
    }
    //成功,自动提示服务端message
    if(success.data.msg){
        Message.success({message:success.data.msg})
    }
    //返回到请求调用的地方
    return success.data;
},error=>{
    if(error.response.status===504||error.response.status===404){
        Message.error({message:'服务器被吃了------'})
    }else if(error.response.status===403){
        Message.error({message:'权限不足,请联系管理员'})
    }else if(error.response.status===401){

        Message.error({message:'尚未登陆,请登陆'})
        router.replace('/')
    }else{
        if(error.response.data.msg){
            // 服务端有错误消息
            Message.error({message:error.response.data.msg})
        }else{
            //服务端没有返回错误信息
            Message.error({message:'未知错误'})
        }

    }
    return;
})

let base='';
//登录请求默认key value形式传递参数,不支持json数据格式传参
export const postKeyValueRequest=(url,params)=>{
    return axios({
        method:'post',
        // 不是单引号,表示变量
        url:`${base}${url}`,
        data:params,
        transformRequest:[function (data){
            let ret='';
            for(let i in data){
                ret+=encodeURIComponent(i)+'='+encodeURIComponent(data[i])+'&'
            }
            return ret;
        }],
        headers:{
            'Content-Type':'application/x-www-form-urlencoded'
        }
    });
}
//封装请求方法,post put get delete,json形式传递数据
// 将其做成插件,不然每次使用都要加载 main.js
export const postRequest=(url,params)=>{
    return axios({
        method:'post',
        url:`${base}${url}`,
        data:params
    })
}
export const putRequest=(url,params)=>{
    return axios({
        method:'put',
        url:`${base}${url}`,
        data:params
    })
}
export const getRequest=(url,params)=>{
    return axios({
        method:'get',
        url:`${base}${url}`,
        data:params
    })
}
export const deleteRequest=(url,params)=>{
    return axios({
        method:'delete',
        url:`${base}${url}`,
        data:params
    })
}

main.js中导入方法


/**
 * 将请求方法挂到vue上,后面需要发送网络请求时,不需要导入api
 */
//1.导入所有的请求方法
import {postRequest} from "@/utils/api";
import {postKeyValueRequest} from "@/utils/api";
import {putRequest} from "@/utils/api";
import {deleteRequest} from "@/utils/api";
import {getRequest} from "@/utils/api";
import {initMenu} from "@/utils/menu";
import 'font-awesome/css/font-awesome.min.css'
//2.将请求方法添加到vue.prototype上
Vue.prototype.postRequest = postRequest;

Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;

Vue.config.productionTip=false

请求转发代理vue.config.js

//代理对象
let proxyObj = {};
//拦截http请求
proxyObj['/']={
    //关闭websocket
    ws:false,
    //目标拦截地址
    target:'http://localhost:8085',
    //属性
    changeOrigin:true,
    //请求地址重写
    pathRewrite:{
        '^/':''
    }
}
module.exports={
    //开发环境
    devServer:{
        host:'localhost',
        //服务本身信息
        port:8081,
        //代理就是上面的代理对象
        proxy:proxyObj
    }
}

Home页title制作 左边导航菜单制作

container容器

NavMenu导航菜单

Home.vue

<template>
  <div>
    <el-container>
      <el-header class="homeHeader">
        <div class="title">
          人事管理系统
        </div>
        <!--    手指效果 src为变量-->
        <el-dropdown class="userInfo" @command="commandHandler">
  <span class="el-dropdown-link">
<!--      头像效果,src为变量-->
    {{user.name}}<i><img :src="user.userface" alt=""></i>
  </span>
          <el-dropdown-menu slot="dropdown">
<!--            command为点击事件,点击回调-->
            <el-dropdown-item command="userinfo">个人中心</el-dropdown-item>
            <el-dropdown-item command="setting">设置</el-dropdown-item>
            <el-dropdown-item command="logout" divided>注销登录</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>

      </el-header>
<!--      container容器,设置index.html的margin和padding-->
      <el-container>
        <el-aside width="200px">
<!--          同一时间只打开一个菜单项-->
<!--          NavMenu导航菜单中,router属性是指是否使用vue-router的模式,启用该模式会在激活导航是以index作为path进行路由跳转-->
          <el-menu router unique-opened>
<!--            <template  v-if="!this.$router.options.routes.hidden">-->
            <el-submenu :index="index+''" v-for="(item,index) in routes"
                         : key="index" >
              <template slot="title" v-if="!item.hidden">
                <i :class="item.iconCls" style="color: #409eff;margin-right: 5px"></i>
                <span>{{ item.name }}</span>
              </template>

  <!--            <el-submenu index="1-4">-->
  <!--              <template slot="title">选项4</template>-->
              <el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{{child.name}}</el-menu-item>
<!--              <el-menu-item index="/test2">选项2</el-menu-item>-->
  <!--            </el-submenu>-->
            </el-submenu>
<!--            </template>-->
          </el-menu>
        </el-aside>
        <el-container>
          <el-main>
            <el-breadcrumb separator-class="el-icon-arrow-right" v-if="this.$router.currentRoute.path!='/home'">
              <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
              <el-breadcrumb-item >{{ this.$router.currentRoute.name }}</el-breadcrumb-item>
            </el-breadcrumb>
            <div class="homeWelcome" v-if="this.$router.currentRoute.path=='/home'">
              欢迎来到人事管理系统
            </div>

            <router-view class="homeRouterView"/>
          </el-main>
        </el-container>
      </el-container>
    </el-container>
  </div>
</template>

<script>
// import {initMenu} from "@/utils/menu";

export default {
  name: "Home",
  data(){
    return{
      //从store中获取菜单json,渲染成菜单
      user:JSON.parse(window.sessionStorage.getItem("user"))
    }
  },
  computed:{
    routes(){
      return this.$store.state.routes;
    }
  },
  methods:{
    // menuClick(index){
    //    this.$router.push(index);
    // },
    commandHandler(cmd){
      // messagebox消息弹框
      if(cmd === 'logout'){
        this.$confirm('此操作将注销登陆, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          // 注销登录
          this.getRequest("/logout");
          //登录数据清空
          window.sessionStorage.removeItem("user"
          )
          //跳转到登录页面
          this.$store.commit('initRoutes',[])
          this.$router.replace("/")
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消操作'
          });
        });
        }
    }
  }
}
</script>

<style>
  .homeRouterView{
    margin-top: 10px;
  }
  .homeWelcome{
    text-align: center;
    font-size: 30px;
    font-family: 黑体, cursive;
    color: #409eff;
    padding-top: 50px;
  }
  .homeHeader{
    background-color: #409eff;
    /*flex布局*/
    display: flex;
    align-items: center;
    /*水平方向往两边走,空白的地方在中间*/
    justify-content: space-between;
    /*防止两边离得太近*/
    padding: 0px 15px;
    /*padding在框的里面*/
    box-sizing: border-box;
  }
  .homeHeader .title{
    font-size: 25px;
    font-family: 黑体,cursive;
    color: #fcfcfc;
  }
  .homeHeader .userInfo{
    cursor: pointer;
  }
  .el-dropdown-link img{
    width: 48px;
    height: 48px;
    border-radius: 24px;
    margin-left: 8px;
  }
  .el-dropdown-link{
    display: flex;
    align-items: center;
  }
</style>

动态加载菜单,从数据库中加载菜单。后端接口


@RestController
@RequestMapping("/system/config")
public class SystemConfigController {
    @Autowired
    MenuService menuService;
    @GetMapping("/menu")
    public List<Menu> getMenusByHrId(){
        return menuService.getMenusByHrId();
    }

}

id应该通过后端查询,前端传进来的数据时不可信的,可能会传到别人的菜单

封装一个menuService进行查询

package org.javagirl.vhr_project.service;

import org.javagirl.vhr_project.mapper.MenuMapper;
import org.javagirl.vhr_project.mapper.MenuRoleMapper;
import org.javagirl.vhr_project.model.Hr;
import org.javagirl.vhr_project.model.Menu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Service
public class MenuService {
    @Autowired
    MenuMapper menuMapper;
    @Autowired
    MenuRoleMapper menuRoleMapper;
    //获取id
    public List<Menu> getMenusByHrId() {
        return menuMapper.getMenusByHrId(((Hr)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId());
    }

//    @Cacheable 缓存,防止每次请求都查数据库
    public List<Menu> getAllMenusWithRole(){
        return menuMapper.getAllMenusWithRole();
    }

    public List<Menu> getAllMenus() {
        return menuMapper.getAllMenus();
    }

    public List<Integer> getMidsByRid(Integer rid) {
        return menuMapper.getMidsByRid(rid);
    }

    @Transactional
    public boolean updateMenuRole(Integer rid, Integer[] mids) {
        menuRoleMapper.deleteByRid(rid);
        Integer result = menuRoleMapper.insertRecord(rid,mids);
        return result==mids.length;
    }
}

<select id="getMenusByHrId" resultMap="Menus2">
    SELECT DISTINCT m1.*,m2.`id` AS id2,m2.`component` AS component2,m2.`enabled` AS enabled2,m2.`iconCls` AS iconCls2,m2.`keepAlive` AS keepAlive2,m2.`name` AS name2,m2.`parentId` AS parentId2,m2.`path` AS path2,m2.`requireAuth` AS requireAuth2  FROM menu m1,menu m2,hr_role hrr,menu_role mr WHERE m1.`id`=m2.`parentId`AND hrr.`hrid`=#{hrid} AND hrr.`rid`=mr.`rid`AND mr.`mid`=m2.`id` AND m2.`enabled`=TRUE ORDER BY m1.`id`,m2.`id`;
  </select>

<resultMap id="Menus2" type="org.javagirl.vhr_project.model.Menu" extends="BaseResultMap">
    <collection property="children" ofType="org.javagirl.vhr_project.model.Menu">
      <id column="id2" property="id" jdbcType="INTEGER" />

      <result column="path2" property="path" jdbcType="VARCHAR" />
      <result column="component2" property="component" jdbcType="VARCHAR" />
      <result column="name2" property="name" jdbcType="VARCHAR" />
      <result column="iconCls2" property="iconCls" jdbcType="VARCHAR" />

      <result column="parentId2" property="parentId" jdbcType="INTEGER" />
      <result column="enabled2" property="enabled" jdbcType="BIT" />
      <association property="meta" javaType="org.javagirl.vhr_project.model.Meta">
        <result column="keepAlive2" property="keepAlive" jdbcType="BIT" />
        <result column="requireAuth2" property="requireAuth" jdbcType="BIT" />
      </association>
    </collection>
  </resultMap>

现在客户端中运行sql成功后再复制到对应的位置,以免出错

根据hr表获取hrid,hr_role表通过hrid获取rid,menu_role中通过rid获取mid,menu表中通过mid获取用户可以操作的menu

得到每个页面的父页面

菜单项接口介绍

菜单项数据加载成功之后,在前端有几个可以存放的地方:

  • sessionStorage 不是很安全
  • localStorage 不是很安全
  • vuex 菜单数据在多个文件引用,放在menu.js。确保数据安全,所有都能访问

vuex 状态管理,数据调用,数据共享。

多个vue文件多次进行切换时使用keepAlive字段管理,但是可能会出现加载混乱的问题

vuex把数据放在公共的地方

vux是一个UI库

安装

npm install vuex

vue.js官网的状态管理中的内容

vuex是一个专为vue.js应用程序开发的状态管理模式,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
/**
 * 最开始的界面:index.js->main.js->App.vue->router.js
 * 当用户注销登陆时,将localStorage中的数据清除
 */
export default new Vuex.Store({
    state:{
        routes:[]
    },
    mutations:{
        initRoutes(state,data){
            state.routes = data;
        }
    },
    actions:{

    }
})

在main.js中引入store文件

import Vue from 'vue'
import ElementUI from 'element-ui';
import App from './App.vue'
import router from './router'
import store from './store'


Vue.config.productionTip=false
Vue.use(ElementUI,{size:'small'});



new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

menu.js

import {getRequest} from "@/utils/api";

// 浏览器刷新后保证菜单栏还在,可以使用路由导航守卫
export const initMenu=(router,store)=>{
    //判断store中的数据是否存在,如果存在,说明这次跳转是正常的跳转,而不是用户按F5或者直接在地址栏输入某个地址进入的。否则去加载菜单。
    if(store.state.routes.length>0){
        return;
    }
    getRequest("/system/config/menu").then(data=>{
        if(data){
            //通过formatRoutes方法将服务器返回的json转为router需要的格式,转component,因为服务端返回的component事一个字符串,router中需要的事一个组件,在firmatRoutes方法中动态的加载需要的组件。
            let fmtRoutes = formatRoutes(data);
            //一方面将数据存入store中,另一方面利用路由中的addRoutes方法将它动态的添加到路由中
            router.addRoutes(fmtRoutes);
            store.commit('initRoutes',fmtRoutes);
        }
    })

}
export const formatRoutes=(routes)=>{
    let fmRoutes=[];
    routes.forEach(router=>{
        //批量定义
        let{
            path,
            component,
            name,
            meta,
            iconCls,
            children
        }=router;
        if(children && children instanceof Array){
            children=formatRoutes(children);
        }
        let fmRouter={
            path:path,
            name:name,
            iconCls:iconCls,
            meta:meta,
            children:children,
            component(resolve) {
                // if (component.startsWith("Home")) {
                //     require(['../views/' + component + '.vue'], resolve);
                // } else if (component == "EmpBasic") {
                //     require(['../views/emp/' + component + '.vue'], resolve);
                // } else if (component == "PerEmp") {
                //     require(['../views/per/' + component + '.vue'], resolve);
                // }else if (component == "SalSobCfg") {
                //     require(['../views/sal/' + component + '.vue'], resolve);
                // } else if (component == "SalSob") {
                //     require(['../views/sal/' + component + '.vue'], resolve);
                // } else if (component == "SysHr") {
                //     require(['../views/sys/' + component + '.vue'], resolve);
                // } else if (component == "SysBasic") {
                //     require(['../views/sys/' + component + '.vue'], resolve);
                // }
                if (component.startsWith("Home")) {
                    require(['../views/' + component + '.vue'], resolve);
                } else if (component.startsWith("Emp")) {
                    require(['../views/emp/' + component + '.vue'], resolve);
                } else if (component.startsWith("Per")) {
                    require(['../views/per/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sal")) {
                    require(['../views/sal/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sta")) {
                    require(['../views/sta/' + component + '.vue'], resolve);
                } else if (component.startsWith("Sys")) {
                    require(['../views/sys/' + component + '.vue'], resolve);
                }
            }
        }
        fmRoutes.push(fmRouter);
    })
    return fmRoutes;
}

前端页面添加并完善菜单请求

views中创建菜单文件.vue

页面刷新,或按f5后菜单还在。

路由导航守卫,全局前置守卫

main.js

import Vue from 'vue'
import ElementUI from 'element-ui';
import App from './App.vue'
import router from './router'
import store from './store'

/**
 * 将请求方法挂到vue上,后面需要发送网络请求时,不需要导入api
 */
//1.导入所有的请求方法
import {postRequest} from "@/utils/api";
import {postKeyValueRequest} from "@/utils/api";
import {putRequest} from "@/utils/api";
import {deleteRequest} from "@/utils/api";
import {getRequest} from "@/utils/api";
import {initMenu} from "@/utils/menu";
import 'font-awesome/css/font-awesome.min.css'
//2.将请求方法添加到vue.prototype上
Vue.prototype.postRequest = postRequest;

Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;

Vue.config.productionTip=false
Vue.use(ElementUI,{size:'small'});

// 全局导航首页,页面跳转前监听
router.beforeEach((to, from, next) => {
  //1. 如果要去的页面是登录页面,直接过
  // 2. 如果不是登录页面,先从store中获取当前的登录状态。如果未登录,通过路由中的meta属性的requireAuth属性判断要去的页面是否需要登录,如果需要登录,调回登录页面,如果不需要登录,直接过。如果已经登陆了,先初始化菜单,再跳转
  if(to.path === '/'){
    next();
  }else{
    // 从sessionStorage中拿到用户登陆数据,判断用户是否登陆
    if(window.sessionStorage.getItem("user")){
      initMenu(router,store);
      next();
    }else{
      // 没有登录
      next('/?redirect='+to.path);
    }
  }
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

Home.vue

computed:{
    routes(){
      return this.$store.state.routes;
    }
  },
<el-aside width="200px">
<!--          同一时间只打开一个菜单项-->
<!--          NavMenu导航菜单中,router属性是指是否使用vue-router的模式,启用该模式会在激活导航是以index作为path进行路由跳转-->
          <el-menu router unique-opened>
<!--            <template  v-if="!this.$router.options.routes.hidden">-->
            <el-submenu :index="index+''" v-for="(item,index) in routes"
                         : key="index" >
              <template slot="title" v-if="!item.hidden">
                <i :class="item.iconCls" style="color: #409eff;margin-right: 5px"></i>
                <span>{{ item.name }}</span>
              </template>

  <!--            <el-submenu index="1-4">-->
  <!--              <template slot="title">选项4</template>-->
              <el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{{child.name}}</el-menu-item>
<!--              <el-menu-item index="/test2">选项2</el-menu-item>-->
  <!--            </el-submenu>-->
            </el-submenu>
<!--            </template>-->
          </el-menu>
        </el-aside>

面包屑作为导航线

前后端分离权限管理

前端跳转页面是为了提高用户体验

真正的权限校验在后端做,SSM框架建议使用shiro,SpringBoot+微服务,建议使用Spring Security

后端接口权限设计

用户发起HTTP请求,后端查询匹配的路径,查看当前用户是否具有该权限

一级菜单不需要用户权限

  1. 根据用户发送的url地址提取需要的角色,
  2. 判断当前用户是否具有相应角色

1.这个类的作用,主要是根据用户传来的请求地址,分析出请求需要的角色

CustomFilterInvocationSecurityMetadataSource

package org.javagirl.vhr_project.config;

/**
 * 这个类的作用,主要是根据用户传来的请求地址,分析出请求需要的角色
 * 获取该地址需要的用户角色
 */
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{

    @Autowired
    MenuService menuService;

    AntPathMatcher antPathMatcher = new AntPathMatcher();

//    请求匹配,没匹配上的登录之后访问
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //获取请求地址
        String requestUrl=((FilterInvocation) object).getRequestUrl();
        //查询数据库中url pattern和role的对应关系
        List<Menu> menus=menuService.getAllMenusWithRole();
        for (Menu menu : menus) {
            //提取当前的请求url,将请求和数据库查询出来的所有路径匹配规则进行匹配
            if(antPathMatcher.match(menu.getUrl(),requestUrl)){
                //获取该路径对应的角色
                List<Role> roles = menu.getRoles();
                String[] str=new String[roles.size()];
                for (int i = 0; i < roles.size(); i++) {
                    str[i] = roles.get(i).getName();
                }
                return SecurityConfig.createList(str);

            }
        }
        //ROLE_LOGIN标记,没匹配上的,都是登陆访问
        return SecurityConfig.createList("ROLE_LOGIN");
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

MenuMapper

//开发中加缓存,使用redis或者@Cacheable
List<Menu> getAllMenusWithRole();
<resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Menu" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="url" property="url" jdbcType="VARCHAR" />
    <result column="path" property="path" jdbcType="VARCHAR" />
    <result column="component" property="component" jdbcType="VARCHAR" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="iconCls" property="iconCls" jdbcType="VARCHAR" />

    <result column="parentId" property="parentId" jdbcType="INTEGER" />
    <result column="enabled" property="enabled" jdbcType="BIT" />
    <association property="meta" javaType="org.javagirl.vhr_project.model.Meta">
      <result column="keepAlive" property="keepAlive" jdbcType="BIT" />
      <result column="requireAuth" property="requireAuth" jdbcType="BIT" />
    </association>
  </resultMap>
<resultMap id="MenuWithRole" type="org.javagirl.vhr_project.model.Menu" extends="BaseResultMap">
    <collection property="roles" ofType="org.javagirl.vhr_project.model.Role">
      <id column="rid" property="id"/>
      <result column="rname" property="name"/>
      <result column="rnameZh" property="nameZh"/>
    </collection>
  </resultMap>
<select id="getAllMenusWithRole" resultMap="MenuWithRole">
    SELECT m.*,r.`id` AS rid,r.`name` AS rname,r.`nameZh` AS rnameZh FROM menu m,menu_role mr,role r WHERE m.`id`=mr.`mid` AND mr.`rid`=r.`id` ORDER BY m.`id`
  </select>
  1. 判断当前哟用户是否具备角色

    package org.javagirl.vhr_project.config;
    
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.authentication.AnonymousAuthenticationToken;
    import org.springframework.security.authentication.InsufficientAuthenticationException;
    import org.springframework.security.config.core.GrantedAuthorityDefaults;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.stereotype.Component;
    
    import java.util.Collection;
    
    /**
     * 判断当前用户是否具有角色,请求是否通过
     */
    @Component
    public class CustomUrlDecisionManager implements AccessDecisionManager {
    
        @Override
        public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
            for (ConfigAttribute configAttribute : configAttributes) {
                //当前请求需要的权限
                String needRole = configAttribute.getAttribute();
                //ROLE_LOGIN标记,没有权限
                if("ROLE_LOGIN".equals(needRole)){
                    if(authentication instanceof AnonymousAuthenticationToken){
                        throw new AccessDeniedException("尚未登录,请登录");
                    }else{
                        return;
                    }
                }
                //当前用户具有的权限
                Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                //查看当前用户的角色列表中是否具备需要的权限
                //假设当前用户具备多个橘色,当前请求需要多个角色。
                //只要用户包含一个请求角色就算授权成功
                for (GrantedAuthority authority : authorities) {
                    if(authority.getAuthority().equals(needRole)){
                        return;
                    }
                }
            }
            throw new AccessDeniedException("权限不足,请联系管理员");
        }
    
        @Override
        public boolean supports(ConfigAttribute configAttribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return true;
        }
    }
    
    

    Hr类

    private List<Role> roles;
    //获取当前用户所具有的角色
        @Override
        //生成json时忽略该属性
        @JsonIgnore
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
            //roles属性描述当前用户的角色
            for (Role role : roles) {
                authorities.add(new SimpleGrantedAuthority(role.getName()));
            }
            return authorities;
        }
    

HrService

//在执行登陆的过程中,这个方法根据用户名取查找用户
    //如果用户不存在,抛出异常,否则返回Hr
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Hr hr = hrMapper.loadUserByUsername(username);
        if(hr == null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        hr.setRoles(hrMapper.getHrRolesById(hr.getId()));
        return hr;

    }
<select id="getHrRolesById" resultType="org.javagirl.vhr_project.model.Role"
          parameterType="java.lang.Integer">
    SELECT r.* FROM role r,hr_role hrr WHERE hrr.`rid`=r.`id` AND hrr.`hrid`=#{id}
  </select>

基础信息设计

<template>
  <el-tabs v-model="activeName" type="card">
    <el-tab-pane label="部门管理" name="depmanager"><DepManager></DepManager></el-tab-pane>
    <el-tab-pane label="职位管理" name="posmanager"><PosManager></PosManager></el-tab-pane>
    <el-tab-pane label="职称管理" name="joblevelmanager"><JobLevelManager></JobLevelManager></el-tab-pane>
    <el-tab-pane label="权限组" name="permissmanager"><PermissManager></PermissManager></el-tab-pane>
  </el-tabs>
</template>

<script>
import DepManager from "@/components/sys/basic/DepManager";
import PosManager from "@/components/sys/basic/PosManager";
import JobLevelManager from "@/components/sys/basic/JobLevelManager";
import PermissManager from "@/components/sys/basic/PermissManager";

export default {
  name: "SysBasic",
  data(){
    return{
      activeName:'depmanager'
    }
  },
  components:{
    //key:value形式,key value相同时,可以省略key
    'DepManager':DepManager,
    PosManager,
    JobLevelManager,
    PermissManager
  }
}
</script>

<style scoped>

</style>

职位管理前端页面

<!--template渲染时会消失,div样式显示不清楚,只能有一个div-->
<!--职位管理-->
<template>
  <div>
    <div>
      <el-input
          size="small"
          class="addPosInput"

          placeholder="添加职位..."
          prefix-icon="el-icon-plus"
          @keydown.enter.native="addPosition"
          v-model="pos.name">
      </el-input>
      <el-button icon="el-icon-plus" size="small" type="primary" @click="addPosition">添加</el-button>
    </div>
    <div class="posManagerMain">
      <el-table
          :data="positions"
          @selection-change="handleSelectionChange"
          size="small"
          border
          stripe
          style="width: 70%">
        <el-table-column
            type="selection"
            width="55">
        </el-table-column>
        <el-table-column
            prop="id"
            label="编号"
            width="55">
        </el-table-column>
        <el-table-column
            prop="name"
            label="职位名称"
            width="180">
        </el-table-column>
        <el-table-column
            prop="createDate"
            width="120"
            label="创建时间">
        </el-table-column>
        <el-table-column
            prop="enabled"
            width="100"
            label="是否启用">
          <template slot-scope="scope">
            <el-tag v-if="scope.row.enabled" type="success">已启用</el-tag>
            <el-tag v-else type="danger">未启用</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button
                size="mini"
                @click="showEditView(scope.$index, scope.row)">编辑</el-button>
            <el-button
                size="mini"
                type="danger"
                @click="handleDelete(scope.$index, scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-button @click="deleteMany" type="danger" size="small" style="margin-top: 8px;" :disabled="multipleSelection.length===0">批量删除</el-button>
    </div>
    <el-dialog
        title="修改职位名称"
        :visible.sync="dialogVisible"
        width="30%"
        >
      <div>
        <div>
          <el-tag>职位名称</el-tag>
          <el-input class="updatePosInput" size="small" v-model="updatePos.name"></el-input>
        </div>
        <div>
          <el-switch
              v-model="updatePos.enabled"
              active-text="启用"
              inactive-text="禁用">
          </el-switch>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
    <el-button size="small" @click="dialogVisible = false">取 消</el-button>
    <el-button size="small" type="primary" @click="doUpdate">确 定</el-button>
  </span>
    </el-dialog>
  </div>
</template>

<script>
import {deleteRequest} from "@/utils/api";

export default {
  name: "PosManager",
  data(){
    return {
      pos:{
        name:''
      },
      positions: [],
      updatePos:{
        name:'',
        enabled:false
      },
      multipleSelection:[],
      dialogVisible:false
    }
  },
  // 组件初始化
  mounted() {
    this.initPositions();
  },
  methods:{
    deleteMany(){
      this.$confirm('此操作将永久删除【'+this.multipleSelection.length+'】条记录, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        let ids='?';
        this.multipleSelection.forEach(item=>{
          ids+='ids='+item.id+'&';
        })
        this.deleteRequest("/system/basic/pos/"+ids).then(resp=>{
          if(resp){
            this.initPositions();
          }
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    },
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },
    showEditView(index,data){
      this.dialogVisible=true;
      //数据拷贝,防止修改取消时出错
      Object.assign(this.updatePos,data);
      // this.updatePos=data;

    },
    doUpdate(){
      this.putRequest("/system/basic/pos/",this.updatePos).then(resp=>{
        if(resp){
          this.initPositions();
          this.updatePos.name='';
          this.dialogVisible=false;
        }

      })
    },
    // 删除职位
    handleDelete(index,data){
      this.$confirm('此操作将永久删除['+data.name+']职位, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteRequest("/system/basic/pos/"+data.id).then(resp=> {
          if (resp) {
            this.initPositions();
          }
        })
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });

    },

    // 添加职位
    addPosition(){
      if(this.pos.name){
        this.postRequest("/system/basic/pos/",this.pos).then(resp=>{
          if(resp){
            this.initPositions();
            this.pos.name='';
          }
        })
      }else{
        this.$message({
          message: '职位名称不可以为空'
        });
      }
    },
    // 初始化数据
    initPositions(){
      this.getRequest("/system/basic/pos/").then(resp=>{
        if(resp){
          this.positions=resp;
        }
      })
    }

  }
}
</script>

<style>
  .updatePosInput{
    width: 200px;
    margin-left: 8px;

  }
  .addPosInput{
    width: 300px;
    margin-right: 8px
  }
  .posManagerMain{
    margin-top: 10px;
  }

</style>

职位管理后端接口

package org.javagirl.vhr_project.controller.system.basic;


import org.javagirl.vhr_project.model.Position;
import org.javagirl.vhr_project.model.RespBean;
import org.javagirl.vhr_project.service.PositionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.annotation.RequestScope;

import java.util.List;

@RestController
@RequestMapping("/system/basic/pos")
public class PositionController {

    @Autowired
    PositionService positionService;

    @GetMapping("/")
    public List<Position> getAllPositions() {
        return positionService.getAllPositions();
    }

    @PostMapping("/")
    public RespBean addPosition(@RequestBody Position position){
        if(positionService.addPosition(position) == 1){
            return RespBean.ok("添加成功");
        }
        return RespBean.error("添加失败");
    }

    @PutMapping("/")
    public RespBean updatePositions(@RequestBody Position position){
        if(positionService.updatePositions(position)==1){
            return RespBean.ok("更新成功");
        }
        return RespBean.error("更新失败");
    }

    @DeleteMapping("/{id}")
    public RespBean deletePosition(@PathVariable Integer id){
        if(positionService.deletePositionById(id)==1){
            return RespBean.ok("删除成功");
        }
        return RespBean.error("删除失败");
    }

    @DeleteMapping("/")
    public RespBean deletePositionByIds(Integer[] ids){
        if(positionService.deletePositionByIds(ids)==ids.length){
            return RespBean.ok("删除成功");
        }
        return RespBean.ok("删除失败");
    }

}

Service

package org.javagirl.vhr_project.service;

import org.javagirl.vhr_project.mapper.PositionMapper;
import org.javagirl.vhr_project.model.Position;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
public class PositionService {

    @Autowired
    PositionMapper positionMapper;
    public List<Position> getAllPositions() {
        return positionMapper.getAllPositions();
    }

    public Integer addPosition(Position position) {
        position.setEnabled(true);
        position.setCreateDate(new Date());
        return positionMapper.insertSelective(position);
    }

    public Integer updatePositions(Position position) {
        return positionMapper.updateByPrimaryKeySelective(position);
    }

    public Integer deletePositionById(Integer id) {
        return positionMapper.deleteByPrimaryKey(id);
    }

    public Integer deletePositionByIds(Integer[] ids) {
        return positionMapper.deletePositionsByIds(ids);
    }
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.javagirl.vhr_project.mapper.PositionMapper" >
  <resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Position" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="createDate" property="createDate" jdbcType="TIMESTAMP" />
    <result column="enabled" property="enabled" jdbcType="BIT" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, name, createDate, enabled
  </sql>
  <select id="getAllPositions" resultMap="BaseResultMap">
    select * from position
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from position
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from position
    where id = #{id,jdbcType=INTEGER}
  </delete>
    <delete id="deletePositionsByIds">
      delete from position where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id}</foreach>
    </delete>
    <insert id="insert" parameterType="org.javagirl.vhr_project.model.Position" >
    insert into position (id, name, createDate, 
      enabled)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{createDate,jdbcType=TIMESTAMP},
      #{enabled,jdbcType=BIT})
  </insert>
  <insert id="insertSelective" parameterType="org.javagirl.vhr_project.model.Position" >
    insert into position
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="name != null" >
        name,
      </if>
      <if test="createDate != null" >
        createDate,
      </if>
      <if test="enabled != null" >
        enabled,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null" >
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="createDate != null" >
        #{createDate,jdbcType=TIMESTAMP},
      </if>
      <if test="enabled != null" >
        #{enabled,jdbcType=BIT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="org.javagirl.vhr_project.model.Position" >
    update position
    <set >
      <if test="name != null" >
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="createDate != null" >
        createDate = #{createDate,jdbcType=TIMESTAMP},
      </if>
      <if test="enabled != null" >
        enabled = #{enabled,jdbcType=BIT},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="org.javagirl.vhr_project.model.Position" >
    update position
    set name = #{name,jdbcType=VARCHAR},
      createDate = #{createDate,jdbcType=TIMESTAMP},
      enabled = #{enabled,jdbcType=BIT}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:32:24  更:2021-07-29 11:35:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 7:12:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码