SpringSecurity入门《之权限控制》

权限控制

SpringSecurity中当无权访问某个资源时,会返回403错误。

  1. 第一种
    在SpringSecurity的配置类中指定。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 1. SpringSecurity配置类
**/
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 配置资源放行和拦截
     *
     * @param security
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity security) throws Exception {

        security.authorizeRequests()                                         
                .antMatchers("地址") // 配置需要角色访问的请求地址
                .hasRole("角色编码") // 设置访问该地址需要的角色
                .antMatchers("地址") // 配置需要权限访问的地址
                .hasAuthority("权限名称") // 设置访问该地址需要的权限
                .anyRequest() // 其他请求
                .authenticated() // 认证后访问}
}

注意,所有权限设置需要在anyRequest的设置之前,否则不生效。

  1. 第二种

在对应controller的方法上使用注解。这种方式需要先在SpringSecurity配置类中使用@EnableGlobalMethodSecurity(prePostEnabled = true)开启。

  • 在SpringSecurity配置类中开启
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity配置类
 */
@Configuration
@EnableWebSecurity
// 启用全局[方法权限控制]功能,并设置prePostEnabled为true。保证@PreAuthority/@PostAuthority/@PreFilter/@PostFilter生效
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {

}
  • controller方法中使用:
    /* 指定角色 */
    @PreAuthorize("hasRole('角色编码')")
    @RequestMapping("请求地址")
    public String saveAdmin(Admin admin) {
        return "";
    }

    /* 指定权限 */
    @PreAuthorize("hasAuthority('权限名称')")
    @RequestMapping("请求地址")
    public String saveAdmin(Admin admin) {
        return "";
    }

注意:这里指定角色时,是不用加“ROLE_”前缀的。

  • 第三种:
    动态指定需要设置的请求地址,并设置对应的角色和权限。
  • 实现WebSecurityConfigurerAdapter 接口的配置类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.intercept.FilterSecurityInterceptor;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MySecurityMetadataSource metadataSource;
    @Autowired
    private MyAccessDecisionManager decisionManager;

    // 其他需要的配置信息可以在后面加上
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        // 配置资源(路径)和权限的对应关系
                        object.setSecurityMetadataSource(metadataSource);
                        // 设置资源(路径)拦截处理,即判断用户是否有访问资源的权限
                        object.setAccessDecisionManager(decisionManager);
                        return object;
                    }
                })
                .and()
                .csrf()
                .disable()
                .formLogin()
                .permitAll()
        ;
    }
}
  • 实现FilterInvocationSecurityMetadataSource接口,自定义匹配url权限的类:
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;

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

@Slf4j
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    // 该方法的作用是获取请求的url在数据库中配置的权限的集合,方法的具体事实需要根据设计修改
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 从数据库加载url对应的角色和权限信息
        List<ConfigAttribute> list = loadResourceDefine(object);
        // 获取当前请求资源
        FilterInvocation fi = (FilterInvocation) object;
        // 获取所有的url
        String url = fi.getRequestUrl();
        // url匹配器,这里需要根据具体设计情况进行实现,matches方法是用来匹配请求的url是否是数据库配置的url的子域。如果设计是全匹配,则这步可以省略,直接做相等判断。
        RequestMatcher requestMatcher = new AntPathRequestMatcher(url + "**");
        if(requestMatcher.matches(fi.getHttpRequest())){
            return list;
        }

        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        // 设置为true
        return true;
    }

}
  • 实现AccessDecisionManager接口,自定义权限校验的类。
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Iterator;

/**
 * 决策管理器
 * decide 方法决定用户是否有权访问url
 */
@Slf4j
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

    /**
     * 判断用户是否有访问指定资源的权限【直接返回表示能够访问,不能访问则抛异常】
     *
     * @param authentication   当前登录用户拥有的权限,如果没有登录则为游客,登录了则为权限
     * @param object           指定的资源,即url
     * @param configAttributes 该资源设置的权限集合
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 如果该资源没有设置任何权限,则可以访问
        if (configAttributes == null) {
            return;
        }
        log.info("访问地址:" + object.toString());
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            // 获取单个权限信息
            String attribute = configAttribute.getAttribute();
            // 和用户拥有的权限进行匹配
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                // 如果能匹配上,说明该用户拥有访问权限
                String authority1 = authority.getAuthority();
                if (attribute.equals(authority1)) {
                    return;
                }
            }
        }
        // 如果都没有匹配上,则说明用户没有访问此资源的权限
        throw new AccessDeniedException("抱歉!您没有访问该资源的权限!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        // 返回true
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        // 返回true
        return false;
    }

}
  • 设置一个无权访问的跳转页面(在SpringSecurity配置类中设置):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * SpringSecurity配置类
**/
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 配置资源放行和拦截
     *
     * @param security
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity security) throws Exception {

        security.authorizeRequests()                                         
                .antMatchers("地址") // 配置需要角色访问的请求地址
                .hasRole("角色编码") // 设置访问该地址需要的角色
                .antMatchers("地址") // 配置需要权限访问的地址
                .hasAuthority("权限名称") // 设置访问该地址需要的权限
                .anyRequest() // 其他请求
                .authenticated() // 认证后访问
                .and()
                .exceptionHandling()                                        // 设置无权访问时异常处理
                // .accessDeniedPage("错误页面地址")                        // 直接跳转到页面,如果需要做处理则使用下面这个
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
                        // 设置页面提示信息
                        request.setAttribute("errorMsg", "错误提示信息");
                        // 请求转发
                        request.getRequestDispatcher("/WEB-INF/page/common/system-error.jsp").forward(request, response);
                    }
                })                                                          // 处理方法[这里是返回一个错误提示页面]}
}

注意:需要将springMVC的异常拦截器取消,否则使用注解时,异常会被springMVC拦截,导致无法到达SpringSecurity中定义的处理方法中。因为在方法中配置时,请求到达SpringSecurity中没有经过springMVC,但是基于注解的则是已经经过了springMVC。

  • 页面上获取错误提示信息
<h3>
     <!-- SpringSecurity无权访问提示 -->
     ${requestScope.errorMsg}
</h3>

一般情况,第一种 和 第二种虽然可以解决问题,但是是硬编码写死的,框架中一般不采取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值