vhr学习整理

vhr学习整理vhrvue cli3 构建 vue 项目 vue js 官网 安装 命令行工具 vue cli 文档 npm 安装 npminstall g vue clivueversio 项目结构 main js gt App vue gt router js gt login vuepackage json 工具包安装 element 插件 npmielement ui S 引入 importEleme

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文件

 
    <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/>  
         
         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>  
                                                     
                                                     
                                                     
                                                     
                                                     
                                                     dependencies> <build>  
                                                     <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.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>  
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    
                                                                    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

 
     
    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>  
     <el-dropdown class="userInfo" @command="commandHandler"> <span class="el-dropdown-link">  
     { 
   {user.name}}<i><img :src="user.userface" alt=""> 
      i>  
       span> <el-dropdown-menu slot="dropdown">  
       <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>  
             <el-container> <el-aside width="200px">  
              
             <el-menu router unique-opened>  
             <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-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{ 
              {child.name}} 
                 el-menu-item>  
                  
                  
                  el-submenu>  
                   
                   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">  
     
    <el-menu router unique-opened>  
    <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-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{ 
     {child.name}} 
        el-menu-item>  
         
         
         el-submenu>  
          
          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> <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

 
     
    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> 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210855.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午11:45
下一篇 2026年3月18日 下午11:46


相关推荐

  • OpenAI开源首个Agent SDK,反击Manus

    OpenAI开源首个Agent SDK,反击Manus

    2026年3月15日
    2
  • 公司怎么建局域网_大型局域网组建教程

    公司怎么建局域网_大型局域网组建教程什么是局域网局域网(LocalAreaNetwork,LAN)是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程

    2022年8月1日
    10
  • pycharm导入pandas没有智能提示

    pycharm导入pandas没有智能提示使用 pycharm 安装并导入 pandas 问题相关代码 importpandas pd read excel test xlsx df shape 运行结果及报错内容 pycharm 没有提示 df 的属性和方法解决方法 就是在参数的后面加上冒号 类型 那么在函数内部使用该变量时 就有相应的提示了 手动标记 df 的类型 就可以智能提示了

    2026年3月27日
    3
  • Response.ContentType 所有类型

    Response.ContentType 所有类型ez=>application/andrew-inset,hqx=>application/mac-binhex40,cpt=>application/mac-compactpro,doc=>application/msword,bin=>application/octet-stream,dms=>applicatio

    2022年7月19日
    16
  • 关于在eclipse中中文汉字乱码的解决方式[通俗易懂]

    关于在eclipse中中文汉字乱码的解决方式[通俗易懂]很多童鞋反应在吧项目导入到eclipse(myeclipse)时中文会有乱码,修改了编码格式后还是乱码,这里给大家介绍一下关于中文乱码时修改编码的注意事项: 当在eclipse中打开一个文件后发现有中文乱码后,千万不能修改这个文件内容,一旦改过这个文件的内容,那怎么修改编码也没用了,只能重新导入。 当打开文件发现乱码后第一步是关闭这个文件,然后在这个文件上右键,选择属性,然后选择编…

    2022年5月26日
    63
  • Python获取秒级时间戳与毫秒级时间戳的方法[通俗易懂]

    Python获取秒级时间戳与毫秒级时间戳的方法[通俗易懂]1、获取秒级时间戳与毫秒级时间戳、微秒级时间戳importtimeimportdatetimet=time.time()print(t)#原始时间数据print(int(t))#秒级时间戳print(int(round(t*1000)))#毫秒级时间戳print(int(round(t*1000000)))#微秒级时间戳输出1648812012.4263625 #原始时

    2026年4月17日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号