0-Security框架的搭建

1、后台Springboot-Security安全框架

在搭建后台管理框架的时候使用的是Security框架,该框架的执行过程是这样的。

用户类创建

首先要创建一个User类,该类要实现UserDetails接口,在该接口中要实现几个方法,最主要的方法是

    /**
     * 收集用户的角色信息,把角色信息放入到集合中
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        ArrayList<GrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

该方法主要是收集用户的角色信息。

Serivice中

登录时候通过用户名去数据库中查询,如果该用户不存在则抛出一个UsernameNotFoundException,如果用户存在的话直接返回用户;

1-方式是这样的,该方法主要是对用户的用户名和密码进行校验

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(hrSevice).passwordEncoder(new BCryptPasswordEncoder());
    }

2-方式是这样的,该方法主要是放行相应的接口,但是不能放行login接口。如果login是登录的url的话。

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("login_p","/static/**");
    }

3-方式是这样的

@Override
    protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests()
              .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                  @Override
                  public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                      o.setSecurityMetadataSource(urlFilterConfig);
                      o.setAccessDecisionManager(webAccessDecisionManager);
                      return o;
                  }
              })
              .and()
              .formLogin().loginPage("/login_p").loginProcessingUrl("/login")
              .usernameParameter("username").passwordParameter("password")
              .successHandler(new AuthenticationSuccessHandler() {
                  @Override
                  public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                      resp.setContentType("application/json;charset=utf-8");
                      ObjectMapper om = new ObjectMapper();
                      PrintWriter out = resp.getWriter();
                      out.write( om.writeValueAsString(RespBean.ok("登录成功!", HrUtils.getCurrentHrInfo())));
                      out.flush();
                      out.close();
                  }
              })
              .failureHandler(new AuthenticationFailureHandler() {
                  @Override
                  public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                      resp.setContentType("application/json;charset=utf-8");
                      ObjectMapper om = new ObjectMapper();
                      PrintWriter out = resp.getWriter();
                      out.write( om.writeValueAsString(RespBean.ok("登录失败!",null)));
                      out.flush();
                      out.close();
                  }
              })

              .permitAll()
              .and()
              .logout()
              .logoutUrl("/logout")
              .permitAll()
              .and()
              .csrf().disable().exceptionHandling().accessDeniedHandler(accessExceptionHandler);
    }

这个带颜色的几个部分别是:

1、所有的请求都学要授权,不然的话没法访问

2、处理具体的授权请求

3、配置用户登录时候的基本信息

4、登录成功或失败的回调函数

5、退出登录时候要处理的事情

6、没有访问权限时候的异常处理

4-处理所有浏览器提交的url

@Component
public class UrlFilterConfig implements FilterInvocationSecurityMetadataSource {

    @Autowired
    MenuService menuService;

    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) o).getRequestUrl(); //拦截所有的url
        AntPathMatcher antPathMatcher = new AntPathMatcher(); //匹配url使用的

        if (requestUrl.equals("/login_p")){  //如果url是以login_p开始的则不需要拦截,直接放行
            return null; //如果是登录页不需要认证
        }

        List<Menu> menus = menuService.getAllMenus(); //查询菜单表中所有的内容,并且把 菜单中的每一个项目都和角色对应上关系,注意查询出来的结果也是倒着排列的哦

        for (Menu menu : menus) {
            //如果请求的url是menu中存在的
            if (antPathMatcher.match(menu.getUrl(), requestUrl) && menu.getRoles().size()>0){  //如果请求的url在菜单中有并且菜单中的url也有相应的角色
                List<Role> roles = menu.getRoles(); //取出该url所具有的角色
                int size = roles.size();
                String[] values = new String[size];

                for (int i = 0; i < roles.size(); i++) { //收集该url所应该具有的角色
                  values[i] = roles.get(i).getName();
                }

                return  SecurityConfig.createList(values);  //返回该url应该所具有的角色给下一级
            }
        }
        //如果请求的url不是数据库中存在的
        return SecurityConfig.createList("ROLE_LOGIN"); //如果请求的url在菜单中没有,返回给一个ROLE_LOGIN信息给下一级
    }

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

    @Override
    public boolean supports(Class<?> aClass) {
        return SecurityConfig.class.isAssignableFrom(aClass);
    }
}

5-是真正处理权限的类

@Component
public class WebAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication auth, Object o, Collection<ConfigAttribute> cas) throws AccessDeniedException, InsufficientAuthenticationException {

        Iterator<ConfigAttribute> iterator = cas.iterator(); //迭代一下,为了能进行下一步

        while (cas.iterator().hasNext()){ 

            ConfigAttribute ca = iterator.next();

            String needRole = ca.getAttribute();

            //访问的路径存在,但是没有登录
            if ("ROLE_LOGIN".equals(needRole)){
                if (auth instanceof AnonymousAuthenticationToken){
                    throw new BadCredentialsException("未登录");
                }
                return;
            }

            //登录了,访问的路径也存在,但是要判断时候又该权限进行访问
            Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)){ //如果具有的角色是之前登录的角色的一种就可以访问
                    return;
                }
            }

        }

        throw new AccessDeniedException("访问权限不足");

    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

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

至此,项目的整体搭建基本ok。

原文地址:https://www.cnblogs.com/gfbzs/p/12346898.html