springboot2.0整合springsecurity前后端分离进行自定义权限控制

  在阅读本文之前可以先看看springsecurity的基本执行流程,下面我展示一些核心配置文件,后面给出完整的整合代码到git上面,有兴趣的小伙伴可以下载进行研究

  使用maven工程构建项目,首先需要引入最核心的依赖,

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

由于这里我们整合的项目进行了前后端分离,所以我们首先需要自定义登录成功和失败,登出成功的自定义处理类

其实就是实现不同的handler即可:1.首先我们来看登录成功的处理类

/**
 * 处理登录验证成功的类
 * @author zhoukebo
 * @date 2018/9/4
 */
@Component
public class FuryAuthSuccessHandler implements AuthenticationSuccessHandler {
    /**Json转化工具*/
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication) throws IOException{
        SysUser userDetails = (SysUser)authentication.getPrincipal();
        System.out.println("管理员 " + userDetails.getUsername() + " 登录");
        Map<String,String> map=new HashMap<>(2);
        map.put("code", "200");
        map.put("msg", "登录成功");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(map));
    }

}

2.登录验证失败的类

/**
 * 处理登录验证失败的类
 * @author zhoukebo
 * @date 2018/9/4
 */
@Component
public class FuryAuthFailureHandler implements AuthenticationFailureHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        System.out.println("登录验证失败");
        Map<String,String> map=new HashMap<>(2);
        map.put("code", "10001");
        map.put("msg", exception.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(map));
    }
}

3.自定义处理注销成功的类

/**
 * 处理注销成功
 * @author zhoukebo
 * @date 2018/9/4
 */
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {

    /**Json转化工具*/
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{
        Map<String,String> map=new HashMap<>(2);
        map.put("code", "200");
        map.put("msg", "登出成功");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(map));
    }
}

4.自定义没有权限的处理类

/**
 * 处理没有权限的类
 * @author zhoukebo
 * @date 2018/9/5
 */
@Component
public class RestAuthAccessDeniedHandler implements AccessDeniedHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        Map<String,String> map=new HashMap<>(2);
        map.put("code", "403");
        map.put("msg", e.getMessage());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(map));
    }
}

然后对springsecurity进行详细的配置需要继承WebSecurityConfigurerAdapter类,下面是配置文件的详情

/**
 * spring Security配置安全控制中心
 *
 * @author zhoukb
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 依赖注入自定义的登录成功处理器
     */
    @Autowired
    private FuryAuthSuccessHandler furyAuthSuccessHandler;
    /**
     * 依赖注入自定义的登录失败处理器
     */
    @Autowired
    private FuryAuthFailureHandler furyAuthFailureHandler;
    /**
     * 依赖注入自定义的注销成功的处理器
     */
    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;

    /**
     * 注册没有权限的处理器
     */
    @Autowired
    private RestAuthAccessDeniedHandler restAuthAccessDeniedHandler;

    /***注入自定义的CustomPermissionEvaluator*/
    @Bean
    public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new CustomPermissionEvaluator());
        return handler;
    }

    /***注入我们自己的登录逻辑验证器AuthenticationProvider*/
    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这里可启用我们自己的登陆验证逻辑
        auth.authenticationProvider(authenticationProvider);
    }

    /**
     * 配置spring security的控制逻辑
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //"/login"不进行权限验证
                .antMatchers("/login").permitAll()
                .antMatchers("/favicon.ico").permitAll()
                .anyRequest().authenticated()   //其他的需要登陆后才能访问
                .and()
                .formLogin()
                //loginProcessingUrl用于指定前后端分离的时候调用后台登录接口的名称
                .loginProcessingUrl("/login")
                //配置登录成功的自定义处理类
                .successHandler(furyAuthSuccessHandler)
                //配置登录失败的自定义处理类
                .failureHandler(furyAuthFailureHandler)
                .and()
                //loginProcessingUrl用于指定前后端分离的时候调用后台注销接口的名称
                .logout().logoutUrl("/logout")
                .logoutSuccessHandler(myLogoutSuccessHandler)
                .and()
                //配置没有权限的自定义处理类
                .exceptionHandling().accessDeniedHandler(restAuthAccessDeniedHandler)
                .and()
                .cors()//新加入
                .and()
                .csrf().disable();// 取消跨站请求伪造防护
    }
}

上面我们配置了自定义的登录逻辑的验证MyAuthenticationProvider,和自定义的权限验证CustomPermissionEvaluator代码如下

/**
 * 实现自己的AuthenticationProvider类,用来自定义用户校验机制
 * @author zhoukebo
 * @date 2018/9/5
 */
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private CustomerDetailService customerDetailService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取表单输入中返回的用户名;
        String userName = (String) authentication.getPrincipal();
        // 获取表单中输入的密码;
        String password = (String) authentication.getCredentials();
        // 这里调用我们的自己写的获取用户的方法;
        UserDetails userInfo = customerDetailService.loadUserByUsername(userName);
        if (userInfo == null) {
            throw new BadCredentialsException("用户名不存在");
        }

        // 这里我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的
        if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) {
            throw new BadCredentialsException("密码不正确");
        }
        // 这里还可以加一些其他信息的判断,比如用户账号已停用等判断。

        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    @Override
    public boolean supports(Class<?> authentication) {
//      这里直接改成retrun true;表示是支持这个执行
        return true;
    }
}
/**
 * 我们需要自定义对hasPermission()方法的处理,
 * 就需要自定义PermissionEvaluator,创建类CustomPermissionEvaluator,实现PermissionEvaluator接口。
 * @author zhoukebo
 * @date 2018/9/5
 */
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    /**
     * 自定义验证方法
     * @param authentication        登录的时候存储的用户信息
     * @param targetDomainObject    @PreAuthorize("hasPermission(‘/hello/**‘,‘r‘)") 中hasPermission的第一个参数
     * @param permission            @PreAuthorize("hasPermission(‘/hello/**‘,‘r‘)") 中hasPermission的第二个参数
     * @return
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        // 获得loadUserByUsername()方法的结果
        SysUser user = (SysUser)authentication.getPrincipal();
        // 获得loadUserByUsername()中注入的权限
        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        // 遍历用户权限进行判定
        for(GrantedAuthority authority : authorities) {
            UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority;
            String permissionUrl = urlGrantedAuthority.getPermissionUrl();
            // 如果访问的Url和权限用户符合的话,返回true
            if(targetDomainObject.equals(permissionUrl)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return false;
    }
}

注意:上面的自定义权限要生效还需要在WebSecurityConfig上面加上注解@EnableGlobalMethodSecurity(prePostEnabled = true)

完成登录逻辑还需要我们实现UserDetailsService接口,以便系统能够根据用户名去获取用户的信息,里面还可加上自己的逻辑

/**
 * 需要自定义UserDetailsService实现spring security的UserDetailsService接口
 * @author zhoukebo
 * @date 2018/9/4
 */
@Service
public class CustomerDetailService implements UserDetailsService {

    @Autowired
    SysUserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<SysRole> roles = user.getRoles();

        //将所有的角色对应的资源权限全部放入user对应的grantedAuthority集合中
        for (SysRole role : roles) {
            List<SysResource> resources = role.getResources();
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            for (SysResource resource : resources) {
                if (resource != null && resource.getResourceName()!=null) {
                    GrantedAuthority grantedAuthority = new UrlGrantedAuthority(resource.getMethodPath(),resource.getResourceName());
                    grantedAuthorities.add(grantedAuthority);
                }
            }
            user.setGrantedAuthority(grantedAuthorities);
        }

        System.out.println("s:" + username);
        return user;
    }
}

以上就完成了springboot和springsecurity的整合工作,demo中包含两种自定义权限验证,有兴趣的小伙伴可以自行在github上面下载下来研究,不懂得可以交流,代码有什么不妥的地方也望大家互相指教

github示例代码

原文地址:https://www.cnblogs.com/zhoukebo/p/9674361.html

时间: 2024-10-11 02:08:07

springboot2.0整合springsecurity前后端分离进行自定义权限控制的相关文章

SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权

新项目引入安全控制 项目中新近添加了Spring Security安全组件,前期没怎么用过,加之新版本少有参考,踩坑四天,终完成初步解决方案.其实很简单,Spring Security5相比之前版本少了许多配置,操作起来更轻量 MariaDb登录配置加密策略 SpringSecurity5在执行登录认证时,需预设加密策略. 坑一:加密策略配置,验密始终不通过,报错401 坑二:本地重写的UserDetailsService实现类在注入的时候找不到,目前图省事直接用了 @Qualifier制定 其

Spring Boot + Vue 前后端分离开发,权限管理的一点思路

在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面. 但是在前后端分离中,页面的跳转统统交给前端去做,后端只提供数据,这种时候,权限管理不能再按照之前的思路来. 首先要明确一点,前端是展示给用户看的,所有的菜单显示或者隐藏目的不是为了实现权限管理,而是为了给用户一个良好的体验,不能依靠前端隐藏控件来实现权限管理,即数据安全不能依靠前端. 这点就像普通的表单提交一样,前端做数据

SpringBoot2.0 整合 SpringSecurity 框架,实现用户权限安全管理

本文源码:GitHub·点这里 || GitEE·点这里 一.Security简介 1.基础概念 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为安全控制编写大量重复代码的工作. 2.核心API解读 1).SecurityContextHolder 最基本的对

关于前后端分离后的路由控制问题

路由控制在多数框架中均有专门的方法,比如ng和node.但是只会使用是不可以的,虽然我连使用都不会.研究它的实现方式,才是正经道路,因为无论框架中的方式都炫酷或者实用,都是从底层开发出来了,那么底层的方法,就是需要了解研究的方法.依此,在以后的使用或者是自定义的时候才能够如臂使指. 那么前端的路由控制总归来说是有两种方法的. 第一种是H5新增的一类API,history.首先来看一下它的诸多方法. //与浏览器后退按钮作用相同 window.history.back(); //跳转到 windo

关于大型网站技术演进的思考(十七)--网站静态化处理—满足静态化的前后端分离(9)

前后端分离的主题虽然讲完了,但是前后端分离的内容并没有结束,本篇将继续前后端分离的问题,只不过这次前后端分离的讲述将会围绕着本系列的主题网站静态化进行.在讲本篇主题之前,我需要纠正一下前后端分离主题讲述中会让朋友们产生误导的地方,这种误导就是对时下流行的一些前后端分离方案(没有使用nodejs的前后端分离方案)的评价问题,其实本人任然觉得不管什么样的前后端分离方案只要成功被实施,并且产生了良好的效果,那么它就是一个成功的前后端分离方案,前面我以一种批判的角度讲述这些前后端分离方案,并不是想在否定

【转】关于大型网站技术演进的思考(十七)--网站静态化处理—满足静态化的前后端分离(9)

前后端分离的主题虽然讲完了,但是前后端分离的内容并没有结束,本篇将继续前后端分离的问题,只不过这次前后端分离的讲述将会围绕着本系列的主题网站静态化进行.在讲本篇主题之前,我需要纠正一下前后端分离主题讲述中会让朋友们产生误导的地方,这种误导就是对时下流行的一些前后端分离方案(没有使用nodejs的前后端分离方案)的评价问题,其实本人任然觉得不管什么样的前后端分离方案只要成功被实施,并且产生了良好的效果,那么它就是一个成功的前后端分离方案,前面我以一种批判的角度讲述这些前后端分离方案,并不是想在否定

SpringSecurity解决跨域问题,在SpringBoot整合SprinSecurity中如何用前后端分离Ajax登录,Ajax登录返回状态200还是近error

先说说SpringSecurity如何实现前后端分离Ajax登录? 今天使用SpringBoot整合SpringSecurity中想使用Ajax替代SpringSecurit的Form表单提交,在这里我们的提交方式还是使用表单提交 http.formLogin().loginProcessingUrl("/authentication/form") loginProcessingUrl方法表示你登录请求的地址,在这里SpringSecurity默认登录页面地址是/login ,填写了u

Springboot2 Vue 前后端分离 整合打包 docker镜像

项目使用springboot2和Vue前后端分离开发模式,再整合,容器化部署. 主要说明下大体的流程,扫除心里障碍,期间遇到的问题请自行解决. 首先说下Vue打包: 1.在Vue项目目录下运行命令打包:npm run build:prod --report 生成需要使用的dist文件,打包后会出现在项目目录下.(目录结构可能会不同) 按照如下方式整合到springboot项目中,resources在main目录下. (结构不同的话)一样拆到static目录下,static下面直接跟img.css

SpringSecurity实现前后端分离项目的登录认证

一.文章简介 本文简要介绍了spring security的基本原理和实现,并基于springboot整合了spring security实现了基于数据库管理的用户的登录和登出,登录过程实现了验证码的校验功能. 二.spring security框架简介 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.主要包括:用户认证(Authentication)和用户授权(Authorization)两个部分.用户认证指的是验证某个用户能