微人事-后端接口权限设计

后端接口权限设计

思路

表hr 用户表
表hr_role

表role 角色

表menu 菜单
表menu_role

登录成功获取一个id 通过id获取角色 通过角色 知道可以操作哪些菜单

/** 是ant风格的路径匹配符 /** 匹配0或者更多的目录
/employee/basic/**
在这里插入图片描述

用户从前台发送http请求->需要通过id获取角色 查看用户是否有权限访问该接口,在有权访问的url中去匹配(拿到http请求后分析地址和url中的哪一个地址是相匹配的)
一级菜单不设置角色

实现

第一步根据用户的请求地址来分析需要的请求角色

package com.akk.vhr.config;

import com.akk.vhr.model.Menu;
import com.akk.vhr.model.Role;
import com.akk.vhr.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

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

// 该类的功能是启动权限访问,根据用户传来的请求地址,分析出请求需要的角色1
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
    @Autowired
    MenuService menuService;
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    // Collection当前请求所需要的角色 Object是FilterInvocation对象
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 这里就是获取到了请求的地址
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        // 然后就是去数据库去匹配对应的角色 先做查询
        List<Menu> allMenusWithRole = menuService.getAllMenusWithRole();
        // 一条一条的遍历比较
        for (Menu menu : allMenusWithRole) {
            // 如果传进来的url在查询的数据中进行比较 当前调符合传进来的url 那么返回这个角色
           if (antPathMatcher.match(menu.getUrl(), requestUrl)) {
               //获取角色
                List<Role> roles = menu.getRoles();
                String[] strings = new String[roles.size()];
                for (int i = 0; i < roles.size(); i++) {
                    strings[i] = roles.get(i).getName();
                }
            // 由于要返回Collection<>类型 所以使用SecurityConfig.createList 他的参数是数组 所以进行了上面的循环遍历
                return SecurityConfig.createList(strings);
            }
        }
        // 没有匹配上的 登录之后访问
        return SecurityConfig.createList("ROLE_LOGIN");
    }

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

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

第二步 判断当前用户是否具备这第一步中返回角色,具备就过,不具备就不能过

package com.akk.vhr.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.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class MyDecisionManager implements AccessDecisionManager {
    // authentication 用户信息  object请求对象  configAttributes第一步的返回值,即用户想要的角色
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : configAttributes) {
            String needRole = configAttribute.getAttribute();
            // 登录即可访问 匿名用户没有角色
            if ("ROLE_LOGIN".equals(needRole)) {
                // 判断是不是你用用户的实例 如果是就是没登录 抛出异常
                if (authentication instanceof AnonymousAuthenticationToken) {
                    throw new AccessDeniedException("尚未登录,请登录");
                }else{
                    return;
                }
            }
            //不是Role_Login 就和用户里的比较
            //获取当前登录用户的角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足,请联系管理员");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

第三步 讲第一第二 引入SecurityConfig中

package com.akk.vhr.config;

import com.akk.vhr.model.Hr;
import com.akk.vhr.model.RespBean;
import com.akk.vhr.service.HrService;
import com.fasterxml.jackson.databind.ObjectMapper;
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.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.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 myFilter;
    @Autowired
    CustomUrlDecisionManager myDecisionManager;
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(hrService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
//                .anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(myDecisionManager);
                        o.setSecurityMetadataSource(myFilter);
                        return o;
                    }
                })
                .and()
                .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/doLogin")
                .loginPage("/login")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        Hr principal = (Hr) authentication.getPrincipal();
                        principal.setPassword(null);
                        RespBean ok = RespBean.ok("登录成功", principal);
                        String s = new ObjectMapper().writeValueAsString(ok);
                        out.write(s);
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        RespBean error = RespBean.error("登陆失败");
                        if (e instanceof LockedException){
                            error.setMsg("账户被锁定请联系管理员");
                        }else if (e instanceof CredentialsExpiredException){
                            error.setMsg("密码过期请联系管理员");
                        }else if(e instanceof AccountExpiredException){
                            error.setMsg("账户过期请联系管理员");
                        }else if(e instanceof DisabledException){
                            error.setMsg("账户被禁用,请联系管理员");
                        }else if(e instanceof BadCredentialsException){
                            error.setMsg("用户名或密码输入错误,请联系管理员");
                        }
                        out.write(new ObjectMapper().writeValueAsString(error));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .logout()
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功")));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .csrf().disable();
    }
}

第四步 给当前角色赋予对应的权限
实体中

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }
private List<Role> roles; 的赋值是在以下部分进行
@Service
public class HrService implements UserDetailsService {
    @Autowired
    HrMapper HrMapper;
    @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;
    }
}

SecurityConfig

    // 如果想要访问login 不用经过Security拦截
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login");
    }

猜你喜欢

转载自blog.csdn.net/weixin_39232166/article/details/106567873