• 【Spring-Security】Re05 权限控制及403处理


    一、访问控制方法及控制项:

    上述配置中的URL后面都离不开的一个访问控制抉择:

    1、全部允许                PermiAll 
    2、全部拒绝                DenyAll
    3、允许匿名访问             Anonymous 也就是普通访问者
    4、允许认证之后访问        Authenticated
    5、必须完全认证?           FullAuthenticated
    6、记住我                  RememberMe

    二、Security权限控制对资源的访问:

    之前的权限配置中我们有设置一个权限的角色属性:

    package cn.zeal4j.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:57
     */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            // 1、通过提供的用户名参数访问数据库,查询记录返回过来,如果记录不存在则抛出异常
            // username = "admin";
            if (!"admin".equals(username)) throw new UsernameNotFoundException("用户名不存在");
    
            // 2、查询出来的凭证是被加密了的,这里是模拟查询的密码
            String encode = passwordEncoder.encode("123456");
    
            // 权限不可以为空,所以需要这么一个工具方法简单实现
            return new User(username, encode, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
        }
    }

    现在我们设置两个特定的资源页面:

    页面的内容区别就是这个:

    <h3>Only admin Allowed</h3>
    <h3>Only vip-01 Allowed</h3>

    控制器处理:

    package cn.zeal4j.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author Administrator
     * @file Spring-Security-Tutorial
     * @create 2020 09 28 13:59
     */
    @Controller
    public class PermitController {
        
        @RequestMapping("admin.page")
        public String toAdminPage() {
            return "permit/admin";
        }
    
        @RequestMapping("vip-01.page")
        public String toVip01Page() {
            return "permit/vip-01";
        }
    }

    现在设置访问权限:

    通过hasAuthority方法进行鉴权,具备资质才会允许访问

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                    // 登陆请求参数设置
                    usernameParameter("username").
                    passwordParameter("password").
                    loginPage("/login.html"). // 设置登陆页面URL路径
                    loginProcessingUrl("/login.action"). // 设置表单提交URL路径
                    // successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                    successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    // failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
                    failureHandler(new FarsAuthenticationFailureHandler("/error.html")); // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式
                    antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    antMatchers("/admin.page").hasAnyAuthority("admin").
                    antMatchers("/vip-01.page").hasAnyAuthority("vip-01").
                    // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                    // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
        }
    }

    访问测试:

    我们默认登录之后赋予的权限中包含admin,所以访问admin.page是没有问题的

    但是如果访问vip-01.page,则会报403错误,访问被禁止了

    三、角色控制:

    上述的权限设置中可以同时设置角色,书写规范是:

    ROLE_角色名称

    例如这样:

    package cn.zeal4j.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:57
     */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            // 1、通过提供的用户名参数访问数据库,查询记录返回过来,如果记录不存在则抛出异常
            // username = "admin";
            if (!"admin".equals(username)) throw new UsernameNotFoundException("用户名不存在");
    
            // 2、查询出来的凭证是被加密了的,这里是模拟查询的密码
            String encode = passwordEncoder.encode("123456");
    
            // 权限不可以为空,所以需要这么一个工具方法简单实现
            return new User(username, encode, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_vip-01"));
        }
    }

    之前访问不了的VIP-01资源,现在权限控制规则可以这样设置:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                    // 登陆请求参数设置
                    usernameParameter("username").
                    passwordParameter("password").
                    loginPage("/login.html"). // 设置登陆页面URL路径
                    loginProcessingUrl("/login.action"). // 设置表单提交URL路径
                    // successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                    successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    // failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
                    failureHandler(new FarsAuthenticationFailureHandler("/error.html")); // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式
                    antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    antMatchers("/admin.page").hasAnyAuthority("admin").
                    /*antMatchers("/vip-01.page").hasAnyAuthority("vip-01").*/
                    antMatchers("/vip-01.page").hasRole("vip-01").
                    // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                    // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
        }
    }

    重启项目运行:

    现在VIP-01资源可以被正常的访问了:

    案例演示只是使用了单一的角色和权限处理,但实际开发的环境下面更为复杂,Security对此也提供好了 多权限 + 多角色的API

    四、IP地址判断:

    在之前的JavaWeb的学习中,我们可以通过请求的HttpServletRequest对象中,获取发送的请求的客户端IP地址

    注意在本机上的一些访问问题:

    以不同的IP地址形式访问,服务端接受到的IP地址也是不一样的

    1、第一种
    http://localhost:8080/
    打印结果 0:0:0:0:0:0:0:1
    
    2、第二种
    http://127.0.0.1:8080/
    打印结果 127.0.0.1
    
    3、第三种
    http://192.168.43.180:8080/
    打印结果 192.168.43.180

    这里新建一个资源,仅限于第三种IP地址符合的控制

    <h3>Only 192.168.43.180 IP-Host Allowed</h3>

    然后配置控制器和权限:

    @RequestMapping("ip.page")
    public String toIpPage() {
        return "permit/ip";
    }

    看看非远程IP地址是否有效

    antMatchers("/ip.page").hasIpAddress("192.168.43.180").

    可以看到,使用Localhost是不被允许访问的

    同样的127.0.0.1地址也不允许

    只有最后的远程IP地址才能被允许访问:

    五、自定义403处理方案

    除了这样的403响应,前后端分离的处理是发送响应状态信息完成的,所以Security允许我们自己设置一个禁止访问的结果的处理

    package cn.zeal4j.handler;
    
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.web.access.AccessDeniedHandler;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author Administrator
     * @file Spring-Security-Tutorial
     * @create 2020 09 28 14:37
     */
    @Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8"); PrintWriter printWriter = httpServletResponse.getWriter(); // json数据 : { "status" : "error" , "message" : "权限不足,访问被禁止!!!" } printWriter.print("{ "status" : "error" , "message" : "权限不足,访问被禁止!!!" }"); printWriter.flush(); printWriter.close(); } }

    然后在配置类中设置,403访问禁止的异常抛出:

    package cn.zeal4j.configuration;
    
    import cn.zeal4j.handler.CustomAccessDeniedHandler;
    import cn.zeal4j.handler.FarsAuthenticationFailureHandler;
    import cn.zeal4j.handler.FarsAuthenticationSuccessHandler;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.access.AccessDeniedHandler;
    
    /**
     * @author Administrator
     * @file IntelliJ IDEA Spring-Security-Tutorial
     * @create 2020 09 27 21:55
     */
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        
        @Autowired
        private AccessDeniedHandler accessDeniedHandler;
    
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.formLogin(). // 设置登陆行为方式为表单登陆
                    // 登陆请求参数设置
                    usernameParameter("username").
                    passwordParameter("password").
                    loginPage("/login.html"). // 设置登陆页面URL路径
                    loginProcessingUrl("/login.action"). // 设置表单提交URL路径
                    // successForwardUrl("/main.page"). // 设置认证成功跳转URL路径 POST请求
                    successHandler(new FarsAuthenticationSuccessHandler("https://www.acfun.cn/")). // 使用自定义的重定向登陆
                    // failureForwardUrl("/error.page");  // 设置认证失败跳转URL路径 POST请求
                    failureHandler(new FarsAuthenticationFailureHandler("/error.html")); // 跨域处理,不需要跳转了
    
            httpSecurity.authorizeRequests().
                    regexMatchers(HttpMethod.POST, "正则表达式").permitAll(). // 还可以对符合正则表达式的请求方式进行要求,这个属性使用来制定请求的方式
                    antMatchers("/**/*.js", "/**/*.css", "/**/images/*.*").permitAll(). // 静态资源放行
                    antMatchers("/login.html").permitAll(). // 登陆页面允许任意访问
                    antMatchers("/error.html").permitAll(). // 失败跳转后重定向的页面也需要被允许访问
                    antMatchers("/admin.page").hasAnyAuthority("admin").
                    /*antMatchers("/vip-01.page").hasAnyAuthority("vip-01").*/
                    antMatchers("/vip-01.page").hasRole("vip-01").
                    antMatchers("/ip.page").hasIpAddress("192.168.43.180").
                    // mvcMatchers("/main.page").servletPath("/xxx").permitAll(). // mvcMatchers资源放行匹配
                    // antMatchers("/xxx/main.page").permitAll(). // 或者多写MSP的前缀
                    anyRequest().authenticated(); // 其他请求均需要被授权访问
    
            // CSRF攻击拦截关闭
            httpSecurity.csrf().disable();
    
            httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    
        }
    }

    403访问测试:

  • 相关阅读:
    Odoo13在Win10(专业版)中的配置
    我在博客园安家了
    2012笔记
    你给我好好发邮件行不行
    事务经典例子
    轻松实现SQL Server与Access、Excel数据表间的导入导出
    SQL大全
    小笔记
    性能优化
    程序中的异常和错误处理
  • 原文地址:https://www.cnblogs.com/mindzone/p/13743745.html
Copyright © 2020-2023  润新知