栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot Security 动态权限入门

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot Security 动态权限入门

废话

本案例是以最简单最简单的方式实现动态权限配置,摒弃各种花里胡哨的代码。

动态权限主要需要实现两个功能:

1、Url访问权限的动态设置

2、用户本身具备的权限动态设置

基础逻辑主要就是用Security作为登录、权限校验,权限允许则访问,权限不允许则提示权限不足。

一、准备工作 1、一个简单的SpringBoot工程

2、引入
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.security
            spring-security-test
            test
        
3、数据源

正常需要三张数据表,用户表、权限表、Url表,然后你就需要读取数据库、连Mybatis、JDBC、Redis巴拉巴拉的,这个跟主题无关,此处用三个写死的类替代就好。

(1)、权限管理类,枚举出来本系统的全部权限

@Service
public class RoseService {
    public final String ADMIN = "ADMIN";
    public final String USER = "USER";
}

(2)、Url管理类,指定那些Url需要哪些权限才能访问

@Service
public class UrlService {

    @Autowired
    RoseService roseService;

    public String[] getRose(String url) {
        if (url.equals("user")) {
            return new String[]{roseService.USER};
        }
        if (url.equals("admin")) {
            return new String[]{roseService.ADMIN, roseService.USER};
        }
        return new String[]{};
    }
}

(3)、用户对象,必须实现Security的UserDetails的6个方法。

@Component
public class UserEntity implements UserDetails {
    private String username;
    private String password;
    private String[] roses;

    

    @Override
    public Collection getAuthorities() {
        List authorities = new ArrayList<>();
        for (String rose : roses) {
            authorities.add(new SimpleGrantedAuthority(rose));
        }
        return authorities;
    }

    

    @Override
    public String getPassword() {
        return password;
    }

    

    @Override
    public String getUsername() {
        return username;
    }

    
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    

    @Override
    public boolean isEnabled() {
        return true;
    }


    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setRose(String[] roses) {
        this.roses = roses;
    }
}

(4)、用户管理类,必须实现Security的UserDatailsService的loadUserByUsername方法,用户登录的时候Security会自动调用该方法

@Service
public class UserService implements UserDetailsService {
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity user = getUser(username);
        if (null == user) {
            throw new UsernameNotFoundException("账户不存在!");
        }
        return user;
    }


    public UserEntity getUser(String username) {
        UserEntity user = null;
        if (username.equals("admin")) {
            user = new UserEntity();
            user.setUsername("admin");
            user.setPassword(new BCryptPasswordEncoder().encode("123456"));
            user.setRose(new String[]{"ADMIN"});
        }
        if (username.equals("user")) {
            user = new UserEntity();
            user.setUsername("user");
            user.setPassword(new BCryptPasswordEncoder().encode("123456"));
            user.setRose(new String[]{"USER"});
        }
        return null;
    }
}
4、一个简单的Controller

设定是这样的,open-不需要权限,admin支持ADMIN权限,user支持ADMIN、USER权限。

@RestController
public class TestController {

    @RequestMapping(value = "open")
    public Object open() {
        return "open";
    }

    @RequestMapping(value = "admin")
    public Object admin() {
        return "admin";
    }

    @RequestMapping(value = "user")
    public Object user() {
        return "user";
    }
}
二、开搞

需要准备最少三个类。

1、WebSecurityFilterInvocationSecuritymetadataSource

用于根据Url获取到该Url需要什么样的权限才能访问

@Component
public class WebSecurityFilterInvocationSecuritymetadataSource implements FilterInvocationSecuritymetadataSource {

    @Autowired
    UrlService urlService;

    private static Map roseMap;

    
    @PostConstruct
    private List getRoses() {
        System.out.println("Url权限数据已加载");
        roseMap = new HashMap<>();
        roseMap.put("/admin", urlService.getRose("admin"));
        roseMap.put("/user", urlService.getRose("user"));
        return null;
    }

    
    @Override
    public Collection getAttributes(Object o) throws IllegalArgumentException {
        String url = ((FilterInvocation) o).getRequestUrl();
        System.out.println("当前请求的Url:" + url);
        String[] roses = roseMap.get(url);
        return SecurityConfig.createList(roses);
    }

    
    @Override
    public Collection getAllConfigAttributes() {
        System.out.println("返回所有定义好的权限资源");
        return null;
    }

    
    @Override
    public boolean supports(Class aClass) {
        System.out.println("是否支持校验");
        return true;
    }
}
2、WebSecurityAccessDecisionManger

用于权限校验,对比传过来的Url需要的权限和当前用户拥有的权限去判定当前用户对当前Url是否有权访问

@Component
public class WebSecurityAccessDecisionManger implements AccessDecisionManager {


    @Override
    public void decide(Authentication authentication, Object o, Collection collection) throws AccessDeniedException, InsufficientAuthenticationException {

        //如果authentication是UsernamePasswordAuthenticationToken实例,说明当前用户已登录。
        if (!(authentication instanceof UsernamePasswordAuthenticationToken)) {
            System.out.println("----------------> 请先登录!");
            throw new AccessDeniedException("请先登录");
        }
        Collection auths = authentication.getAuthorities();

        System.out.println("Url需要的权限:" + collection.toString());
        System.out.println("用户当前的权限:" + auths.toString());

        for (ConfigAttribute configAttribute : collection) {
            //循环校验,有权限满足就证明是可以访问的
            for (GrantedAuthority authority : auths) {
                if (configAttribute.getAttribute().equals(authority.getAuthority())) {
                    return;
                }
            }
        }
        System.out.println("----------------> 权限不足!");
        throw new AccessDeniedException("权限不足!");
    }

    
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        System.out.println("configAttribute是否支持校验");
        return true;

    }

    
    @Override
    public boolean supports(Class aClass) {
        System.out.println("aClass是否支持校验");
        return true;

    }
}
3、WebSecurityConfig

最核心的Security配置类

@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/500").permitAll()//不过滤
                .antMatchers("/403").permitAll()//不过滤
                .antMatchers("/404").permitAll()//不过滤
                //其他不过滤的Url,略
                .anyRequest() //任何其它请求
                .authenticated() //都需要身份认证
                .withObjectPostProcessor(new ObjectPostProcessor() {
                    @Override
                    public  O postProcess(O object) {
                        //自定义权限校验
                        object.setSecuritymetadataSource(new WebSecurityFilterInvocationSecuritymetadataSource());
                        object.setAccessDecisionManager(new WebSecurityAccessDecisionManger());
                        return object;
                    }
                })
                .and()
                // 开启表单登录,即登录页
                .formLogin()
                // 自定义登录页,未配置下启用默认登录页
                // .loginPage("/login_page")
                // 登录成功之后跳转的页面
                .loginProcessingUrl("/index")
                // 登录参数-用户名
                .usernameParameter("username")
                // 登录参数-密码
                .passwordParameter("password")
//                 登录成功回调函数-返回登陆成功的JSON信息
                .successHandler((request, response, authentication) -> {
                    System.out.println("-----登录成功-----> ");
                })
//                 登录失败回调函数-返回登陆失败的JSON信息
                .failureHandler((request, response, e) -> {

                    System.out.println("-----登录失败-----> ");
                })
                // 和登录相关的接口都不需要认证即可访问
                .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                // 配置注销登录请求URL,默认为 "/logout"
                .logoutUrl("/logout")
                // 是否清除身份认证信息,默认为true,表示清除
                .clearAuthentication(true)
                // 是否使session失效,默认为true
                .invalidateHttpSession(true)
                // 删除指定的cookie信息,可以传入多个key
                .deletecookies("JSESSIONID")
                // 注销回调函数,可以处理数据清除工作
                .addLogoutHandler((request, response, authentication) -> {
                    System.out.println("-----注销回调----->");
                    this.getcookieMsg(request);
                })
                // 注销成功回调函数
                .logoutSuccessHandler((request, response, authentication) -> {
                    System.out.println("-----注销成功----->");
                    // 注销成功后重定向到登录页
                    response.sendRedirect("/login");
                })
                .and()
                .csrf().disable();

//        http.addFilterBefore(authorizationSecurityInterceptor, FilterSecurityInterceptor.class);
    }

    
    private void getcookieMsg(HttpServletRequest request) {
        cookie[] cookies = request.getcookies();
        if (null == cookies || cookies.length < 1) {
            System.out.println("------> cookie已全部清除!");
        } else {
            System.out.println("---cookie---> ");
        }
    }

    //解决静态资源被拦截的问题
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(15);
    }

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/340020.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号