【认证与授权】Spring Security的授权流程
上一篇我们简单的分析了一下认证流程,通过程序的启动加载了各类的配置信息。接下来我们一起来看一下授权流程,争取完成和前面简单的web基于sessin的认证方式一致。由于在授权过程中,我们预先会给用于设置角色,关于如果加载配置的角色信息这里就不做介绍了,上一篇的加载过程中我们可以发现相关的信息。
本篇依旧基于spring-security-basic
配置角色信息
配置用户及其角色信息的方式很多,我们这次依旧采取配置文件的方式,不用代码或其他的配置方式,在之前的配置用户信息的地方application.yml,添加用户的角色信息。
spring:
security:
user:
name: admin
password: admin
roles: ADMIN,USER
这样我们就完成了最简单的用户角色赋予。在加载用户信息时我们知道会生成一个User对象,将其用户名、密码、权限信息封装进去。
这里需要注意一下关于role信息的加载
public UserBuilder roles(String... roles) {
List<GrantedAuthority> 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
1、获取用户信息
我们在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
可以看到,控制台打印的三段信息是完全一样的。说明这里通过三种方式获取的用户信息是一致的。既然可以获取到当前登录的用户信息,接下来我们就可以通过用户信息的判断来决定其是否可以访问那些接口。
2、自定义拦截器
上一步我们通过三种方式获取到了认证用户的信息,这里我们将设计一个拦截器来控制用户的访问权限。我们先设计两个接口,一个只能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/**");
}
}
3、注解方式判断
通过拦截器的方式配置,看上去非常的繁琐,如果我需要给某个接口添加一个角色访问权限,还需要去修改拦截器中的判断逻辑。当然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<String> roleSet = getAuthoritySet(); for (String role : roles) {
String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
if (roleSet.contains(defaultedRole)) {
return true;
}
} return false;
}
到这里,我们已经完成了基于拦截器和注解方式的接口授权设置,基本上都是在零配置的基础上完成的。我们写发现了,好像不太容易扩展信息,例如application.yml中没办法同时设置多个用户,认证成功后我想跳转到自定义的页面或者自定义的信息。别急,从下一篇开始,我们将逐步对代码进行改造,一步一步打造成你想满足的各种需求
(完)
【认证与授权】Spring Security的授权流程的更多相关文章
- SpringBoot集成Spring Security(授权与认证)
⒈添加starter依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifact ...
- [权限管理系统篇] (五)-Spring security(授权过程分析)
欢迎关注公众号[Ccww笔记],原创技术文章第一时间推出 前言 权限管理系统的组件分析以及认证过程的往期文章: Spring security (一)架构框架-Component.Service.Fi ...
- Spring Security OAuth2 授权失败(401) 问题整理
Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考 http://www.ruanyifeng.com/blog/2014/0 ...
- Spring Security OAuth2 授权码模式
背景: 由于业务实现中涉及到接入第三方系统(app接入有赞商城等),所以涉及到第三方系统需要获取用户信息(用户手机号.姓名等),为了保证用户信息的安全和接入方式的统一, 采用Oauth2四种模式之一 ...
- springboot学习之授权Spring Security
SpringSecurity核心功能:认证.授权.攻击防护(防止伪造身份) 涉及的依赖如下: <dependency> <groupId>org.springframework ...
- Spring Security 用户授权原理分析
本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12. 本文接着Spring Security 用户认证原理分析进行分析,此时用户已完成 ...
- 单点登录(SSO)解决方案之 CAS客户端与Spring Security集成
接上篇:单点登录(SSO)解决方案之 CAS服务端数据源设置及页面改造 Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制 ...
- Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战
一. 前言 [APP 移动端]Spring Security OAuth2 手机短信验证码模式 [微信小程序]Spring Security OAuth2 微信授权模式 [管理系统]Spring Se ...
- Spring Security OAuth2.0认证授权六:前后端分离下的登录授权
历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...
随机推荐
- JSP学习笔记(三)
jsp与javabean 编写javabean 创建与使用bean 获取和修改bean的属性 javabean是一种java类,是通过封装属性和方法成为具有某种功能或处理某个业务的对象,简称bean, ...
- 如何更换 App icon
每逢重大节日,App icon 就要跟一波"潮流"做一次更换,节日过后再换回普通.如何保证这两次切换流程丝滑顺畅呢? 应用内需要更换的 icon 包括两处,一个是 App 主 ic ...
- Java引用的分类
Java引用分为强引用.软引用.弱引用和虚引用. 强引用就是指在程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被 ...
- Mysql数据库错误代码大全
Mysql数据库错误代码大全 出现较多的一些网页代码提示的意思: 1016错误:文件无法打开,使用后台修 ...
- 鬼吹灯之龙岭迷窟百度云迅雷BT在线观看免费全集
看视频搜索微信公众号:qyw1091 还记得去年5月11日在高家堡举行的<鬼吹灯之龙岭迷窟>开机仪式吗?时隔数月,这部网剧于4月1日将在腾讯视频全网独播了! . <鬼吹灯之龙岭迷窟& ...
- 跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
提到MySQL的事务,我相信对MySQL有了解的同学都能聊上几句,无论是面试求职,还是日常开发,MySQL的事务都跟我们息息相关. 而事务的ACID(即原子性Atomicity.一致性Consiste ...
- canvas压缩、裁切图片和格式转换的方法
按照大小压缩图片,或者按照特定分辨率裁切图片,转为blob数据.自动处理ios中可能存在的照片偏差90°问题. 例如,获取300*300大小的头像,实现以下效果: 使用方式: <!-- 引入js ...
- easy-mock 本地部署(挤需体验三番钟,里造会干我一样,爱象节款mock)
前言 很多小伙伴问我怎么在自己公司的项目里面添加配置mock,在vue项目里面都知道怎么配置mock,在大型前端项目里面就一脸疑惑了. 我就回答他,你今天会在vue项目里面用,那天换公司是用angul ...
- Array(数组)对象-->数组的访问
1.访问数组: 通过指定数组名以及索引号码,你可以访问某个特定的元素. 格式: 数组对象名[下标] 例如:arr[0] 就是访问数组第一个值 var arr = new Array(3); arr[ ...
- JUC——检视阅读
JUC--检视阅读 参考资料 JUC知识图参考 JUC框架学习顺序参考 J.U.C学习总结参考,简洁直观 易百并发编程,实践操作1,不推荐阅读,不及格 JUC文章,带例子讲解,可以学习2 Doug L ...