@

本教程是基于SpringMVC而创建的,不适用于WebFlux。(如果你不知道这两者,可以忽略这句提示)

提出一个需求

所有的技术是为了解决实际问题而出现的,所以我们并不空谈,也不去讲那么多的概念。在这样一个系统中,有三个接口,需要授权给三种权限的人使用,如下表:

接口地址 需要的权限描述 可访问的权限组名称
visitor/main 不需要权限,也不用登录,谁都可以访问
admin/main 必须登录,只有管理员可以访问 ADMIN
user/main 必须登录,管理员和用户权限都能访问 USER和ADMIN

解决方案:

  • 在Controller中判断用户是否登录和用户的权限组判断是否可以访问

    这是最不现实的解决方案,可是我刚进公司时的项目就是这样设计的,当时我还觉得很高大尚呢。

  • 使用Web应用的三大组件中和过滤器(Filter)进行判断

    这是正解,SpringSecurity也正是用的这个原理。如果你的项目足够简单,建议你直接使用这种方式就可以了,并不需要集成SpringSecurity。这部分的示例在代码中有演示,自己下载代码查看即可。

  • 我们可以直接使用SpringSecurity框架来解决这个问题

使用SpringSecurity进行解决

​ 网上的教程那么多,但是讲的都不清不楚。所以,请仔细阅读下段这些话,这要比后边的代码重要。

​ SpringSecurity主要有两部分内容:

  • 认证 (你是谁,说白了就是一个用户登录的功能,帮我们验证用户名和密码)
  • 授权 (你能干什么,就是根据当前登录用户的权限,说明你能访问哪些接口,哪些不能访问。)

这里的登录是对于浏览器访问来说的,因为如果是前后端分离时,使用的是Token进行授权的,也可以理解为登录用户,这个后边会讲。这里只是为了知识的严谨性才提到了这点

SpringSecurity和SpringBoot结合

1. 首先在pom.xml中引入依赖:

<!-- 不用写版本,继承Springboot的版本-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 配置用户角色和接口的权限关系

是支持使用xml进行配置的,但是在SpringBoot中更建议使用Java注解配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 配置用户权限组和接口路径的关系
* 和一些其他配置
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 对请求进行验证
.antMatchers("/visitor/**").permitAll()
.antMatchers("/admin/**").hasRole("ROLE_ADMIN") // 必须有ADMIN权限
.antMatchers("/user/**").hasAnyRole("ROLE_USER", "ROLE_ADMIN") //有任意一种权限
.anyRequest() //任意请求(这里主要指方法)
.authenticated() //// 需要身份认证
.and() //表示一个配置的结束
.formLogin().permitAll() //开启SpringSecurity内置的表单登录,会提供一个/login接口
.and()
.logout().permitAll() //开启SpringSecurity内置的退出登录,会为我们提供一个/logout接口
.and()
.csrf().disable(); //关闭csrf跨站伪造请求
} }

上边的配置主要内容有两个:

  1. 配置访问三个接口(实际上不仅仅是3个,/**是泛指)需要的权限;
  2. 配置了使用SpringSecurity的内置/login和/loginout接口(这个是完全可以自定义的)
  3. 权限被拒绝后的返回结果也可以自定义,它当权限被拒绝后,会抛出异常

说明:

  1. 上边的配置中,其实就是调用http的这个对象的方法;
  2. 使用.and()只为了表示一上配置结束,并满足链式调用的要求,不然之前的对象可能并不能进行链式调用
  3. 这个配置在SpringBoot应用启动的时候就会调用,也就是会将这些配置加载进内存,当用户调用对应的接口的时候,就会判断它的角色是否可以调用这个接口,流程图如下(我觉得图要比文字更能说明过程):

3. 配置用户名和密码

​ 配置了上边的接口和用户权限角色的关系后,就是要配置我们的用户名和密码了。如果没有正确的用户名和密码,神仙也登录不上去。

​ 关于这个,网上的教程有各种各样的配置,其实就一个接口,我们只需要实现这个接口中的方法就可以了。接口代码如下:

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
/**
* 在登录的时候,就会调用这个方法,它的返回结果是一个UserDetails接口类
*/
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

​ 来看一下这个接口,如果想扩展,可以自己写一个实现类,也可以使用SpringSecurity提供的实现

public interface UserDetails extends Serializable {
// 用户授权集合
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}

​ UserDetailsServicer接口的实现类

@Configuration
public class UserDetailsServiceImpl implements UserDetailsService {
/**
* 这个方法要返回一个UserDetails对象
* 其中包括用户名,密码,授权信息等
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/**
* 将我们的登录逻辑写在这里
* 我是直接在这里写的死代码,其实应该从数据库中根据用户名去查
*/
if (username == null) {
//返回null时,后边就会抛出异常,就会登录失败。但这个异常并不需要我们处理
return null;
}
if (username.equals("lyn4ever")) {
//这是构造用户权限组的代码
//但是这个权限上加了ROLE_前缀,而在之前的配置上却没有加。
//与其说这不好理解,倒不如说这是他设计上的一个小缺陷
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(authority);
//这个user是UserDetails的一个实现类
//用户密码实际是lyn4ever,前边加{noop}是不让SpringSecurity对密码进行加密,使用明文和输入的登录密码比较
//如果不写{noop},它就会将表表单密码进行加密,然后和这个对比
User user = new User("lyn4ever", "{noop}lyn4ever", list);
return user;
}
if (username.equals("admin")) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
SimpleGrantedAuthority authority1 = new SimpleGrantedAuthority("ROLE_ADMIN");
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(authority);
list.add(authority1);
User user = new User("admiin", "{noop}admin", list);
return user;
} //其他返回null
return null;
}
}

4.进行测试

​ 分别访问上边三个接口,可以看到访问结果和上边的流程是一样的。

总结:

  • 仔细阅读上边的那个流程图,是理解SpringSecurity最重要的内容,代码啥的都很简单;上边也就两个类,一个配置接口与角色的关系,一个实现了UserDetailsService类中的方法。
  • 前边说了,SpringSecurity主要就是两个逻辑:
    • 用户登录后,将用户的角色信息保存在服务器(session中);
    • 用户访问接口后,从session中取出用户信息,然后和配置的角色和权限进行比对是否有这个权限访问
  • 上述方法中,我们只重写了用户登录时的逻辑。而根据访问接口来判断当前用户是否拥有这个接口的访问权限部分,我们并没有进行修改。所以这只适用于可以使用session的项目中。
  • 对于前后端分离的项目,一般是利用JWT进行授权的,所以它的主要内容就在判断token中的信息是否有访问这个接口的权限,而并不在用户登录这一部分。
  • 解决访问的方案有很多种,选择自己最适合自己的才是最好了。SpringSecurity只是提供了一系列的接口,他自己内部也有一些实现,你也可以直接使用。
  • 上边配置和用户登录逻辑部分的内容是完全可以从数据库中查询出来进行配置的。

代码地址

在SpringBoot中使用SpringSecurity的更多相关文章

  1. 在SpringBoot中对SpringSecurity的基本使用

    参考文献: Spring Security Architecture What is authentication in Spring Security? Spring Security是一个能够为基 ...

  2. 记录一下在SpringBoot中实现简单的登录认证

    代码参考博客: https://blog.csdn.net/weixin_37891479/article/details/79527641 在做学校的课设的时候,发现了安全的问题,就不怀好意的用户有 ...

  3. springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)

    本文使用springboot+mybatis+SpringSecurity 实现用户权限数据库管理 实现用户和角色用数据库存储,而资源(url)和权限的对应采用硬编码配置. 也就是角色可以访问的权限通 ...

  4. SpringBoot中yaml配置对象

    转载请在页首注明作者与出处 一:前言 YAML可以代替传统的xx.properties文件,但是它支持声明map,数组,list,字符串,boolean值,数值,NULL,日期,基本满足开发过程中的所 ...

  5. 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧

    做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...

  6. springboot中swaggerUI的使用

    demo地址:demo-swagger-springboot springboot中swaggerUI的使用 1.pom文件中添加swagger依赖 2.从github项目中下载swaggerUI 然 ...

  7. spring-boot+mybatis开发实战:如何在spring-boot中使用myabtis持久层框架

    前言: 本项目基于maven构建,使用mybatis-spring-boot作为spring-boot项目的持久层框架 spring-boot中使用mybatis持久层框架与原spring项目使用方式 ...

  8. 由浅入深学习springboot中使用redis

    很多时候,我们会在springboot中配置redis,但是就那么几个配置就配好了,没办法知道为什么,这里就详细的讲解一下 这里假设已经成功创建了一个springboot项目. redis连接工厂类 ...

  9. Springboot中使用AOP统一处理Web请求日志

    title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...

随机推荐

  1. Dubbo之服务暴露

    前言 本文 Dubbo 使用版本2.7.5 Dubbo 通过使用dubbo:service配置或@service在解析完配置后进行服务暴露,供服务消费者消费. Dubbo 的服务暴露有两种: 远程暴露 ...

  2. DOTNET CORE源码分析之ServiceDescriptor

    ServiceDescriptor在.net core中的作用就是DI中注入服务元素的描述.每一个元素核心内容部分包括需要注入的服务元素的类型ServiceType,它对应的接口(如果有的话)Impl ...

  3. Linux常用命令 - top命令详解(重点)

    21篇测试必备的Linux常用命令,每天敲一篇,每次敲三遍,每月一循环,全都可记住!! https://www.cnblogs.com/poloyy/category/1672457.html top ...

  4. FormData/Go分片/分块文件上传

    FormData 接口提供了一种表示表单数据的键值对的构造方式,经过它的数据可以使用 XMLHttpRequest.send() 方法送出,本接口和此方法都相当简单直接.如果送出时的编码类型被设为 & ...

  5. Python GUI wxPython StaticText控件背景色透明

    import wx class TransparentStaticText(wx.StaticText): """ 重写StaticText控件 "" ...

  6. 类与对象 CG作业1. 复数类Comple 。

    问题描述]一个完整的复数由实数部分和序数部分构成,请定义一个复数类,含有两个私有属性realPart和imagPart分别表示复数的实部和虚部,三个成员函数:initComplex实现复数成员变量的初 ...

  7. Django 视图笔记

    视图 概述 作用:视图接受web请求,并响应 本质:python中的一个函数 响应: 网页;重定向:错误视图(404.500) json数据 url配置 配置流程 1:指定根基url配置文件 sett ...

  8. 16. nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "auditUnitName"

    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver:handleHttpMessageNotRead ...

  9. Activiti入门案例

    一.流程定义 Activiti-Designer 使用 Palette(画板) 在eclipse 或 idea 中安装activiti-designer 插件即可使用,画板中包括以下结点: Conne ...

  10. 超参数、验证集和K-折交叉验证

    本文首发自公众号:RAIS ​前言 本系列文章为 <Deep Learning> 读书笔记,可以参看原书一起阅读,效果更佳. 超参数 参数:网络模型在训练过程中不断学习自动调节的变量,比如 ...