@

本教程是基于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. 聊聊order by的工作机制

    总结写在前面: 1. 介绍了orderBy的两种算法流程:全字段排序 和 rowid排序. 2. rowid排序 相比 全字段排序,参与排序字段较少,耗内存较少,多一步回表,如果内存够的情况下MySQ ...

  2. 信号与系统实验序章0——MATLAB基础命令入门

    本次开启新的系列,关于用Matlab实现常见信号和函数的生成和变换. 同时如果没有MATLAB基础,那么可以跟着本文一步一步学习Matlab的相关操作,本文旨在记录在信号与系统课程中MATLAB的学习 ...

  3. shell脚本基础-语法

    一 变量 [root@T_FOOT-Home2-ZZZ01 ~]# a=hello [root@T_FOOT-Home2-ZZZ01 ~]# echo $a hello [root@T_FOOT-Ho ...

  4. jenkins-gitlab-harbor-ceph基于Kubernetes的CI/CD运用(一)

    注:这部分的学习还是要靠自己多点点 多尝试尝试 这部分19年3月份我是玩的很溜的,一年没用,基本忘光光了. 学习要温故而知新! 流程拓扑图 前提准备 部署应用服务 部署kubernetes 集群:ht ...

  5. 使用tomcat运行时提示some characters cannot be mapped using iso-8859-1 character encoding异常

    今天第一次使用java进行jsp项目搭建,也是第一次使用tomcat.tomcat是运行java web的一个小型服务器,属于Apache的一个开源免费的服务. 在运行web 的时候,我们就要先配置好 ...

  6. spring boot devtools热部署

    问题1: Springloaded 在springboot2的maven的pom.xml 无法找到 解决方法:在idea通过View->Tool Windows->Maven Projec ...

  7. hGame2020第二周第一题题解

    Description: Cosmos通过两个小时速成了PHP+HTML,他信心满满的写了一个博客,他说要从博客后台开始......(flag在根目录, 禁止使用任何扫描器) Challenge Ad ...

  8. [极客大挑战 2019]BabySQL 1

    考点就是一系列的sql注入操作 和 replace函数过滤 进入页面如图 ​ 基础过滤测试 union .select .information_schema试试有没有被过滤 ?username=ad ...

  9. 如何获取主键返回值(MySQL、Oracle)

    添加用户.返回主键 --场景:在执行新增用户sql后,service层返回新增用户的主键值(与mybatis一起使用) insert into user(username, sex, birthday ...

  10. CF1327D Infinite Path 题解

    原题链接 太坑了我谔谔 简要题意: 求一个排列的多少次幂能达到另一个排列.排列的幂定义见题.(其实不是新定义的,本来就是这么乘的) 很显然,这不像快速幂那样可以结合律. 既然这样,就从图入手. 将 \ ...