SpringBoot 集成 Spring Security 自定义权限逻辑备忘

继上一次记录《SpringBoot 集成 Spring Security 自定义认证逻辑备忘》之后,这次记录一下SpringBoot 集成 Spring Security 自定义权限逻辑

源码位置:码云地址CSDN下载地址

主要分为三步:1、用户自定义认证通过后获取数据库栏目地址列表;2、添加自定义权限校验器 SecurityPermissionEvaluator; 3、注入自定义 SecurityPermissionEvaluator

具体代码如下:

1、在类 SecurityAuthenticationProvider 中添加栏目链接的设置

package com.lakey.framework.security.provider;

import com.lakey.common.constant.Constants;
import com.lakey.common.utils.LakeyTextUtils;
import com.lakey.common.utils.MD5Utils;
import com.lakey.framework.security.domain.SecurityUserInfo;
import com.lakey.framework.security.handler.SecurityAuthenticationSuccessHandler;
import com.lakey.project.sys.sysMenu.domain.SysMenu;
import com.lakey.project.sys.sysMenu.service.ISysMenuService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * SpringSecurity 自定义 AuthenticationProvider
 *
 * @author LiMing
 * @date 2019-03-26
 */
@Component
public class SecurityAuthenticationProvider implements AuthenticationProvider {

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 注入我们自己定义的用户信息获取对象
     */
    @Qualifier("securityUserDetailsService")
    @Autowired
    private UserDetailsService userDetailService;
    @Autowired
    private ISysMenuService sysMenuService;

    /**
     * 认证用户信息
     *
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = authentication.getName();// 这个获取表单输入中的用户名
        String password = (String) authentication.getCredentials();// 这个是表单中输入的密码

        /** 判断用户是否存在 */
        SecurityUserInfo userInfo = (SecurityUserInfo) userDetailService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;
        if (userInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        logger.info("userInfo : " + userInfo.toString());

        /** 判断密码是否正确 */
        String encodePwd = MD5Utils.hash(userInfo.getSalt().concat(password));
        if (!userInfo.getPassword().equals(encodePwd)) {
            throw new BadCredentialsException("密码不正确");
        }

        /** 判断账号是否停用 */
        if (!userInfo.isEnabled()) {
            throw new DisabledException("账户不可用");
        }

        /** 判断账号是否过期 */
        if (!userInfo.isAccountNonExpired()) {
            throw new DisabledException("账户已过期");
        }

        /** 设置栏目链接 */
        List<SysMenu> menus = SecurityAuthenticationProvider.getMenus(sysMenuService, userInfo.getId());
//        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        List<GrantedAuthority> authorities = new ArrayList <>();
        for (SysMenu menu : menus) {
            if (menu != null && LakeyTextUtils.isNotEmpty(menu.getUrl())&& LakeyTextUtils.isNotEmpty(menu.getPerms())) {
                String authorityStr = menu.getUrl() + "__" + menu.getPerms();
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authorityStr);
                authorities.add(grantedAuthority);
            }
        }
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    /**
     * 执行支持判断
     *
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return true;// 返回 true ,表示支持执行
    }

    /**
     * 返回管理员拥有的可见栏目列表
     *
     * @param sysMenuService
     * @param userId
     * @return
     */
    public static List<SysMenu> getMenus(ISysMenuService sysMenuService, Integer userId) {
        List<SysMenu> menus = new ArrayList<>();
        // 查询所有可见目录列表
        if (Constants.ADMINISTRATOR_ID.equals(userId)) {// 系统默认超级管理员账户拥有所有可见栏目权限
            SysMenu paramMenu = new SysMenu();
            paramMenu.setVisible("1");
            menus = sysMenuService.selectSysMenuList(paramMenu);
        }
        // 查询管理员可见目录列表
        else {
            menus = sysMenuService.selectMenuListByUserId(userId);
        }

        return menus;
    }
}

2、添加自定义权限校验器 SecurityPermissionEvaluator

package com.lakey.framework.security.provider;

import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Collection;

/**
 * SpringSecurity 自定义 PermissionEvaluator
 *
 * @author LiMing
 * @date 2019-03-27
 */
@Component
public class SecurityPermissionEvaluator implements PermissionEvaluator {

    @Override
    public boolean hasPermission(Authentication authentication, Object targetUrl, Object targetPermission) {
        Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) authentication.getAuthorities();
        for(GrantedAuthority authority : authorities) {
            if(authority.getAuthority().equals(targetUrl + "__" + targetPermission)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) {
        return false;
    }
}

3、注入自定义 SecurityPermissionEvaluator

package com.lakey.framework.security.config;

import com.lakey.framework.security.provider.SecurityPermissionEvaluator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

/**
 * Spring Security 配置类
 *
 * @author LiMing
 * @date 2019-03-26
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${security.login.loginUrl}")
    private String loginUrl;// 登录地址
    @Value("${security.login.loginErrUrl}")
    private String loginErrUrl;// 登录认证失败地址

    /**
     * 注入自定义的 AuthenticationProvider
     */
    @Autowired
    private AuthenticationProvider provider;
    /**
     * 注入自定义的 AuthenticationSuccessHandler
     */
    @Autowired
    private AuthenticationSuccessHandler securityAuthenticationSuccessHandler;
    /**
     * 注入自定义的 AuthenticationFailureHandler
     */
    @Autowired
    private AuthenticationFailureHandler securityAuthenticationFailHandler;

    /**
     * 访问权限配置
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /* 禁用csrf模式 */
        http.csrf().disable();

        /* 允许同源 iframe */
        http.headers().frameOptions().sameOrigin();

        /* 登录页面链接、登录表单链接、登录失败页面链接配置 */
        http.formLogin().loginPage(loginUrl).loginProcessingUrl("/login-process")
            .successHandler(securityAuthenticationSuccessHandler)
            .failureHandler(securityAuthenticationFailHandler)
            .permitAll()  // 登录页面链接、登录表单链接、登录失败页面链接配置
            .and()
            .authorizeRequests()
            .antMatchers("/css/**").permitAll() // css 静态资源配置
            .antMatchers("/fonts/**").permitAll() // fonts 静态资源配置
            .antMatchers("/img/**").permitAll() // img 静态资源配置
            .antMatchers("/js/**").permitAll() // js 静态资源配置
            .antMatchers("/plugins/**").permitAll() // js 插件静态资源配置
            .antMatchers(loginErrUrl).permitAll() // 登录错误链接配置
            .anyRequest().authenticated();

        /* 默认的退出的url地址是/logout,默认的退出成功跳转的url地址是/login?logout */
        http.logout();
    }

    /**
     * 动态认证
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(provider);
    }

    /**
     * 注入自定义 PermissionEvaluator
     */
    @Bean
    public DefaultWebSecurityExpressionHandler myWebSecurityExpressionHandler(){
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new SecurityPermissionEvaluator());
        return handler;
    }
}

猜你喜欢

转载自blog.csdn.net/nangongyanya/article/details/88876309