• 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示例代码

  • 相关阅读:
    CLR(Common Language Runtime)
    六个经典的英语面试问题
    XML基本知识(三)
    vc++中各种字符串(转载)
    winform窗体间传值
    jQuery实现按比例缩放图片
    .net中几个名词解释
    XML Schema 定义
    C#中TreeView组件使用方法初步
    微软电话面试题
  • 原文地址:https://www.cnblogs.com/zhoukebo/p/9674361.html
Copyright © 2020-2023  润新知