【认证与授权】Spring Security的授权流程

简介:

【认证与授权】Spring Security的授权流程

上一篇我们简单的分析了一下认证流程,通过程序的启动加载了各类的配置信息。接下来我们一起来看一下授权流程,争取完成和前面简单的web基于sessin的认证方式一致。由于在授权过程中,我们预先会给用于设置角色,关于如果加载配置的角色信息这里就不做介绍了,上一篇的加载过程中我们可以发现相关的信息。

本篇依旧基于spring-security-basic

1|0配置角色信息
配置用户及其角色信息的方式很多,我们这次依旧采取配置文件的方式,不用代码或其他的配置方式,在之前的配置用户信息的地方application.yml,添加用户的角色信息。

spring: security: user: name: admin password: admin roles: ADMIN,USER
这样我们就完成了最简单的用户角色赋予。在加载用户信息时我们知道会生成一个User对象,将其用户名、密码、权限信息封装进去。

这里需要注意一下关于role信息的加载

public UserBuilder roles(String... roles) { List authorities = new ArrayList<>( roles.length); for (String role : roles) { Assert.isTrue(!role.startsWith("ROLE_"), () -> role + " cannot start with ROLE_ (it is automatically added)"); authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); } return authorities(authorities); }
也就是说我们上方配置的ADMIN,USER会被转化成ROLE_ADMIN,ROLE_USER

2|01、获取用户信息
我们在BasicController类中添加一个获取认证用户信息的接口

@RequestMapping("/getUser") public String api(HttpServletRequest request) { // 方式一 Principal userPrincipal = request.getUserPrincipal(); UsernamePasswordAuthenticationToken user = ((UsernamePasswordAuthenticationToken) userPrincipal); System.out.println(user.toString()); // 方式二 SecurityContext securityContext = SecurityContextHolder.getContext(); System.out.println(securityContext.getAuthentication()); // 方式三 Object context = request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); SecurityContext securityContext1 = (SecurityContext) context; System.out.println(securityContext1.getAuthentication()); return user.toString(); }
我们从session中去获取用户的信息,然后拿到其授权信息就可以做相应的判断了request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");这一段代码我们找到是在HttpSessionSecurityContextRepository.saveContext(SecurityContext context)中放入的,SPRING_SECURITY_CONTEXT是其维护的常量,这样我们就有可以根据这个key去获取当前的会话信息了。

当然我们还有另外的获取用户信息的方式还记得我们在AbstractAuthenticationProcessingFilter这个核心过滤器中的successfulAuthentication方法

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { if (logger.isDebugEnabled()) { logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); rememberMeServices.loginSuccess(request, response, authResult); // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( authResult, this.getClass())); } successHandler.onAuthenticationSuccess(request, response, authResult); }
这里将其认证成功的结果信息放入到上下文中 SecurityContextHolder.getContext().setAuthentication(authResult);那我们也是可以直接通过其get方法获取SecurityContextHolder.getContext();

登陆后直接访问接口localhost:8080/getUser

org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bade0105: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E4C77C8791C314B7B14F796B0DD38F13; Granted Authorities: ROLE_ADMIN
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bade0105: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E4C77C8791C314B7B14F796B0DD38F13; Granted Authorities: ROLE_ADMIN
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@bade0105: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffbcba8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: E4C77C8791C314B7B14F796B0DD38F13; Granted Authorities: ROLE_ADMIN

可以看到,控制台打印的三段信息是完全一样的。说明这里通过三种方式获取的用户信息是一致的。既然可以获取到当前登录的用户信息,接下来我们就可以通过用户信息的判断来决定其是否可以访问那些接口。

3|02、自定义拦截器
上一步我们通过三种方式获取到了认证用户的信息,这里我们将设计一个拦截器来控制用户的访问权限。我们先设计两个接口,一个只能admin角色用户才可以访问,一个只能user角色用户才可以访问

@RequestMapping("/api/admin") public String adminApi(HttpServletRequest request){ Principal principal = request.getUserPrincipal(); String name = principal.getName(); return "管理员:" + name + "你好,你可以访问/api/admin"; } @RequestMapping("/api/user") public String userApi(HttpServletRequest request){ Principal principal = request.getUserPrincipal(); String name = principal.getName(); return "普通用户:" + name + "你好,你可以访问/api/user"; }
我们设计了两个接口,通过url来区别不同角色访问的结果,我们再设计一个拦截器,这里我们可以直接参考前面的文章 基于session的认证方式 中定义的拦截器

public class AuthenticationInterceptor extends HandlerInterceptorAdapter { private final static String USER_SESSION_KEY = "SPRING_SECURITY_CONTEXT"; // 前置拦截,在接口访问前处理 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object attribute = request.getSession().getAttribute(USER_SESSION_KEY); if (attribute == null) { writeContent(response,"匿名用户不可访问"); return false; } else { SecurityContext context = (SecurityContext) attribute; Collection<? extends GrantedAuthority> authorities = context.getAuthentication().getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals("ROLE_ADMIN") && request.getRequestURI().contains("admin")){ return true; } if (authority.getAuthority().equals("ROLE_USER") && request.getRequestURI().contains("user")){ return true; } } writeContent(response,"权限不足"); return false; } } //响应输出 private void writeContent(HttpServletResponse response, String msg) throws IOException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=utf‐8"); PrintWriter writer = response.getWriter(); writer.write(msg); } }

同时生效该拦截器

@Configuration public class WebSecurityConfig implements WebMvcConfigurer { // 添加自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/api/**"); } }
4|03、注解方式判断
通过拦截器的方式配置,看上去非常的繁琐,如果我需要给某个接口添加一个角色访问权限,还需要去修改拦截器中的判断逻辑。当然Spring Security也提供了非常方便的注解模式去控制接口,需要修改哪个接口的角色访问,直接在接口上修改就可以了

@PreAuthorize("hasRole('ADMIN')") @RequestMapping("/api2/admin") public String admin2Api(String message){ return "hello : " + message; } @PreAuthorize("hasRole('USER')") @RequestMapping("/api2/user") public String user2Api(String message){ return "hello : " + message; }
非常的简单,一个注解就帮我们解决了拦截器中完成的事情,其实他们的原理是差不多的。不过这里有几个需要关注的点

@PreAuthorize注解的生效,需要提前开启的。需要在@EnableGlobalMethodSecurity(prePostEnabled = true) 注解中生效,因为PreAuthorize 默认是false
@PreAuthorize是支持表达式方式进行设置的,我用的是hasRole。是其内置的表达式库SecurityExpressionRoot中的方法
hasRole最终调用的是hasAnyAuthorityName的方法,这里会有一个缺省的前缀,当前你也可以写成hasRole('ROLE_ADMIN')的。并且是变长数组,我们还可一进行多角色的判断例如:hasRole('ROLE','USER')

private boolean hasAnyAuthorityName(String prefix, String... roles) { Set roleSet = getAuthoritySet(); for (String role : roles) { String defaultedRole = getRoleWithDefaultPrefix(prefix, role); if (roleSet.contains(defaultedRole)) { return true; } } return false; }
到这里,我们已经完成了基于拦截器和注解方式的接口授权设置,基本上都是在零配置的基础上完成的。我们写发现了,好像不太容易扩展信息,例如application.yml中没办法同时设置多个用户,认证成功后我想跳转到自定义的页面或者自定义的信息。别急,从下一篇开始,我们将逐步对代码进行改造,一步一步打造成你想满足的各种需求

(完)

EOF

本文作者:黑米面包派
本文链接:https://www.cnblogs.com/wujiwen/p/12792094.html

相关文章
|
1月前
|
XML 前端开发 Java
深入了解Spring MVC工作流程
深入了解Spring MVC工作流程
|
2月前
|
存储 安全 Java
SpringBoot搭建Spring Security 入门
SpringBoot搭建Spring Security 入门
107 0
|
1月前
|
存储 安全 Java
Spring Boot整合Spring Security--学习笔记
Spring Boot整合Spring Security--学习笔记
52 0
|
10天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
3天前
|
安全 Java 数据安全/隐私保护
使用Spring Security进行Java身份验证与授权
【4月更文挑战第16天】Spring Security是Java应用的安全框架,提供认证和授权解决方案。通过添加相关依赖到`pom.xml`,然后配置`SecurityConfig`,如设置用户认证信息和URL访问规则,可以实现应用的安全保护。认证流程包括请求拦截、身份验证、响应生成和访问控制。授权则涉及访问决策管理器,如基于角色的投票。Spring Security为开发者构建安全应用提供了全面且灵活的工具,涵盖OAuth2、CSRF保护等功能。
|
4天前
|
安全 前端开发 Java
Spring Security的授权管理器实现
Spring Security的授权管理器涉及用户登录后的token验证和权限检查。当用户携带token访问时,框架会验证token合法性及用户访问权限。自定义授权管理器`TokenAuthorizationManager`需实现`AuthorizationManager&lt;RequestAuthorizationContext&gt;`接口,处理校验逻辑,包括解析token、判断用户角色与访问资源的匹配。配置中在`SecurityConfig`注册该管理器以生效。测试表明,具有不同角色的用户可访问相应权限的资源,否则返回403错误。
116 4
|
4天前
|
安全 Java Spring
Spring Security的授权&鉴权
该文档介绍了授权和鉴权的概念,主要分为Web授权和方法授权。Web授权通过URL拦截进行,而方法授权利用注解控制权限,粒度更细但耦合度高。在Web授权的案例中,展示了如何在Spring Security中对特定URL设置角色控制。此外,还列举了Spring Security内置的控制操作方法,如permitAll()、denyAll()和hasRole()等,用于定义不同类型的用户访问权限。
106 7
|
9天前
|
安全 Java 数据库
Spring Security加密解密
Spring Security加密解密
|
30天前
|
安全 Java 数据库
Spring Security 权限管理详解与案例
Spring Security 是 Spring 框架中用于提供认证和访问控制的模块。它保护了成千上万的应用程序,使其免受未经授权的访问。本文将详细介绍 Spring Security 的权限管理功能,并通过一个实际案例来展示其用法。
63 0
|
1月前
|
JSON Java 数据库连接
【spring(五)】SpringMvc总结 SSM整合流程
【spring(五)】SpringMvc总结 SSM整合流程