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

SpringSecurity权限管理框架系列(六)-Spring Security框架自定义配置类详解(二)之authorizeRequests配置详解

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

SpringSecurity权限管理框架系列(六)-Spring Security框架自定义配置类详解(二)之authorizeRequests配置详解

1、预置演示环境

这个演示环境继续沿用
SpringSecurit权限管理框架系列(五)-Spring Security框架自定义配置类详解(一)之formLogin配置详解的环境。

2、自定义配置类之请求授权详解

http.authorizeRequests()主要是对url进行访问权限控制,通过这个方法来实现url授权操作。
http.authorizeRequests()也支持链式写法,举例:
​ http.authorizeRequests() .url匹配规则1.权限控制方法1 .url匹配规则2.权限控制方法2...
如图
匹配顺序规则

​在所有匹配规则中取所有规则的交集。配置顺序影响了之后授权效果。

越是具体的应该放在前面,越是笼统的应该放到后面。

访问控制包含访问控制url匹配、访问控制方法、角色判断、权限判断

2.1 访问控制url匹配 2.1.1 anyRequest()

anyRequest(),表示匹配所有的url请求

配置类代码示例:

http.authorizeRequests()
        // 匹配所有的请求,并且所有请求都需要登录认证
        .anyRequest().authenticated();
2.1.2 antMatcher(String regx)

1)antMatcher(String regx),传递一个ant表达式参数,表示匹配所有满足ant表达式的请求

ant表达式中特殊字符解释

规则解释说明
匹配一个字符
*匹配0个或多个字符
**匹配0个或多个目录

配置类代码示例:

http.authorizeRequests()
        // 允许登录页面匿名访问
        .antMatchers("/showLogin", "/errPage").anonymous()
        // 所有的静态资源允许匿名访问
        .antMatchers(
                "/css
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("admin", "superAdmin")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("system,system:user,system:user:list"))
                .and()
                .withUser("common")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("common")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("good,good:cate,good:cate:list"));

配置类如下

http.authorizeRequests()
    // 用户具有system:user权限时允许访问/role
    .antMatchers(HttpMethod.GET, "/role").hasAuthority("system:user")
2.3.4 hasAnyAuthority()

配置类如下

http.authorizeRequests()
    // 用户具有system:user权限时允许访问/role
    .antMatchers(HttpMethod.GET, "/role").hasAnyAuthority("system", "system:user")
2.3.5 access()

上面实现的访问控制的方法都可以使用access()来代替,因为他们本质上都是调用了access(),见下图

配置类如下

http.authorizeRequests()
      // 用户具有system:user权限时允许访问/role
    .antMatchers(HttpMethod.GET, "/role").access("hasAuthority('system'"))
    // 用户具有system:user或system权限时允许访问/role
    .antMatchers(HttpMethod.GET, "/role").access("hasAnyAuthority('system', 'system:user'"))
2.4 使用注解进行角色权限控制

首先如果要启动spring security提供的角色权限注解的话,需要在配置类上添加
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)注解,这样才能开启
@Secured和@PreAuthorize注解

2.4.1 @Secured注解的使用
package com.kkarma.web.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

    @GetMapping("showLogin")
    public String showLogin(){
        return "login";
    }

    @Secured("admin")
    @RequestMapping("home")
    public String home(){
        return "home";
    }

    @RequestMapping("errPage")
    public String errPage(){
        return "error_page";
    }

    @RequestMapping("role")
    public String role(){
        return "role";
    }

    @PreAuthorize("hasRole('admin')")
    @RequestMapping("index")
    public String index(){
        return "index";
    }
}

这里出现了一个bug, 控制器的方法上添加了@Secured(“ROLE_admin”)注解之后,访问仍提示403,这是为什么呢?后面再说这个问题怎么解决。

2.4.2 @PreAuthorize注解的使用
package com.kkarma.web.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

    @GetMapping("showLogin")
    public String showLogin(){
        return "login";
    }

    @RequestMapping("home")
    public String home(){
        return "home";
    }

    @RequestMapping("errPage")
    public String errPage(){
        return "error_page";
    }

    @RequestMapping("role")
    public String role(){
        return "role";
    }
    
    // 直接在类或者方法上添加@PreAuthorize()并传递权限判断方法的字符串和参数
    @PreAuthorize("hasRole('admin')")
    @RequestMapping("index")
    public String index(){
        return "index";
    }
}
2.4.3 @postAuthorize注解的使用

这个注解的主要作用就是将权限判断后置, 什么意思呢, @Secured和@PreAuthorize这两个注解都是对于request来进行判断, 而@postAuthorize这个注解是基于response的响应来进行授权操作的。
比如用户发来一个请求,服务器响应了一个数据, 我就针对这个响应数据跟我的预期结果是不是相符, 如果相符, 说明有权限访问, 如果不相符,说明权限不足,这里简单演示一下
演示代码很简单,一个字符串数据包含in和out两个元素,随机获取一个元素,当取出的元素是in时允许用户访问,其他则无权限

package com.kkarma.web.controller;

import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Random;


@Controller
public class LoginController {

    @GetMapping("showLogin")
    public String showLogin(){
        return "login";
    }

//    @Secured("admin")
    @RequestMapping("home")
    public String home(){
        return "home";
    }

    @RequestMapping("errPage")
    public String errPage(){
        return "error_page";
    }

    @RequestMapping("role")
    public String role(){
        return "role";
    }

    @PreAuthorize("hasRole('admin')")
    @RequestMapping("index")
    public String index(){
        return "index";
    }

    @PostAuthorize("returnObject=='in'")
    @RequestMapping("getStr")
    @ResponseBody
    public String getString() {
        String[] arr = {"in", "out"};
        return arr[new Random().nextInt(2)];
    }
}

响应结果获取到"in",匹配,说明you权限访问

响应结果获取到"out",不匹配,说明权限不足, 抛出403异常

2.5 自定义403异常返回处理

首先, 自定义403异常处理器

package com.kkarma.handler;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setHeader("Content-Type","application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("{"code":"403","msg":"权限不足,无法访问!"}");
        out.flush();
        out.close();
    }
}

其次, 在spring security自定义配置类中注入该处理器对象,并通过http设置

package com.kkarma.config;

import com.kkarma.handler.CustomAccessDeniedHandler;
import com.kkarma.web.service.impl.MyUserDetailsServiceImpl;
import org.apache.coyote.Adapter;
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.authentication.AuthenticationManager;
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.WebSecurityConfigurer;
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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        
         // http.csrf().disable();

        
        http.formLogin()
                // 前端登录表单用户名别名, 从参数user中获取username参数取值
                .usernameParameter("user")
                // 前端登录表单密码别名, 从参数passwd中获取password参数取值
                .passwordParameter("passwd")
                // 当http请求的url是/login时,进行我们自定义的登录逻辑
                .loginProcessingUrl("/login")
                // 自定义登录的前端控制器
                .loginPage("/showLogin")
                // 设置登录成功的跳转链接
                // .successForwardUrl("/home");
                // 通过successHandler处理器进行登录成功之后的逻辑处理
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        System.out.println("登录成功,页面即将跳转...");
                        Collection authorities = authentication.getAuthorities();
                        authorities.forEach(System.out::println);
                        response.sendRedirect("/home");
                    }
                })
                // 设置登录失败的跳转链接
                // .failureForwardUrl("/errPage");
                // 通过failureHandler处理器进行登录失败之后的逻辑处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        e.printStackTrace();
                        System.out.println("登录失败,页面即将跳转到默认失败页...");
                        response.sendRedirect("/errPage");
                    }
                })
                .and()
                // 403权限不足异常使用自定义403处理器处理
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        
        http.authorizeRequests()
                // 允许GET请求登录页面匿名访问
                .antMatchers(HttpMethod.GET, "/showLogin", "/errPage").anonymous()
                // 匹配所有名称以demo结尾的js并允许匿名访问
                .regexMatchers(HttpMethod.GET, ".+demo[.]js").anonymous()
                // 用户具有admin角色时允许访问/role
                .antMatchers(HttpMethod.GET, "/role").hasRole("admin")
                // 用户具有system:user权限时允许访问/role
                .antMatchers(HttpMethod.GET, "/role").hasAuthority("system:user")
                // 所有的静态资源允许匿名访问
                .antMatchers(
                        "/css
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("admin", "superAdmin")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("system,sytem:user,system:user:list"))
                .and()
                .withUser("common")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("common")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("good,good:cate,good:cate:list"));
    }

    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
}

重启项目, 再次访问/getStr, 当无权限访问时就是使用自定义的403进行处理响应。

2.6 用户退出

通过spring security框架我们可以自己自定义用户退出逻辑

具体的配置类信息

package com.kkarma.config;

import com.kkarma.handler.CustomAccessDeniedHandler;
import com.kkarma.web.service.impl.MyUserDetailsServiceImpl;
import org.apache.coyote.Adapter;
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.authentication.AuthenticationManager;
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.WebSecurityConfigurer;
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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        
         http.csrf().disable();

        
        http.formLogin()
                // 前端登录表单用户名别名, 从参数user中获取username参数取值
                .usernameParameter("user")
                // 前端登录表单密码别名, 从参数passwd中获取password参数取值
                .passwordParameter("passwd")
                // 当http请求的url是/login时,进行我们自定义的登录逻辑
                .loginProcessingUrl("/login")
                // 自定义登录的前端控制器
                .loginPage("/showLogin")
                // 设置登录成功的跳转链接
                // .successForwardUrl("/home");
                // 通过successHandler处理器进行登录成功之后的逻辑处理
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        System.out.println("登录成功,页面即将跳转...");
                        Collection authorities = authentication.getAuthorities();
                        authorities.forEach(System.out::println);
                        response.sendRedirect("/home");
                    }
                })
                // 设置登录失败的跳转链接
                // .failureForwardUrl("/errPage");
                // 通过failureHandler处理器进行登录失败之后的逻辑处理
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        e.printStackTrace();
                        System.out.println("登录失败,页面即将跳转到默认失败页...");
                        response.sendRedirect("/errPage");
                    }
                })
                .and()
                // 403权限不足异常使用自定义403处理器处理
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        // 用户退出登录处理配置
        http.logout().
                // 设置用户退出的url
                logoutUrl("/logout")
                // 用户退出成功之后的跳转链接
                .logoutSuccessUrl("/showLogin")
                // 自定义用户的退出逻辑处理器, 可以处理认证信息、session、cookies等信息
                .addLogoutHandler(new LogoutHandler() {
                    @Override
                    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
                        // 这里可以自定义用户退出的处理逻辑
                        System.out.println("退出成功...");
                    }
                })
                // 是否清除认证状态,默认为true
                .clearAuthentication(true)
                // 是否销毁HttpSession对象,默认为true
                .invalidateHttpSession(true);


        
        http.authorizeRequests()
                // 允许GET请求登录页面匿名访问
                .antMatchers(HttpMethod.GET, "/showLogin", "/errPage").anonymous()
                // 匹配所有名称以demo结尾的js并允许匿名访问
                .regexMatchers(HttpMethod.GET, ".+demo[.]js").anonymous()
                // 用户具有admin角色时允许访问/role
                .antMatchers(HttpMethod.GET, "/role").hasRole("admin")
                // 用户具有system:user权限时允许访问/role
                .antMatchers(HttpMethod.GET, "/role").hasAuthority("system:user")
                // 所有的静态资源允许匿名访问
                .antMatchers(
                        "/css
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("admin", "superAdmin")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("system,sytem:user,system:user:list"))
                .and()
                .withUser("common")
                .password(bCryptPasswordEncoder().encode("123456"))
                .roles("common")
                .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList("good,good:cate,good:cate:list"));
    }

    
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
}

在首页home.html添加一个退出链接, 演示一下效果

重启项目, 测试一下
登录成功

点击用户退出,成功跳转到登录页面

这中间可能会出现退出登录抛出异常404,这是因为默认开启了csrf, 在配置类中关闭csrf重启即可解决

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

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

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