spring security是spring家族的一个安全框架,入门简单。对比shiro,它自带登录页面,自动完成登录操作。权限过滤时支持http方法过滤。

在新手入门使用时,只需要简单的配置,即可实现登录以及权限的管理,无需自己写功能逻辑代码。

但是对于现在大部分前后端分离的web程序,尤其是前端普遍使用ajax请求时,spring security自带的登录系统就有一些不满足需求了。

因为spring security有自己默认的登录页,自己默认的登录控制器。而登录成功或失败,都会返回一个302跳转。登录成功跳转到主页,失败跳转到登录页。如果未认证直接访问也会跳转到登录页。但是如果前端使用ajax请求,ajax是无法处理302请求的。前后端分离web中,规范是使用json交互。我们希望登录成功或者失败都会返回一个json。况且spring security自带的登录页太丑了,我们还是需要使用自己的。

spring security一般简单使用:

web的安全控制一般分为两个部分,一个是认证,一个是授权。

认证管理:

就是认证是否为合法用户,简单的说是登录。一般为匹对用户名和密码,即认证成功。

在spring security认证中,我们需要注意的是:哪个类表示用户?哪个属性表示用户名?哪个属性表示密码?怎么通过用户名取到对应的用户?密码的验证方式是什么?

只要告诉spring security这几个东西,基本上就可以了。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { }

事实上只要继承WebSecurityConfigurerAdapter ,spring security就已经启用了,当你访问资源时,它就会跳转到它自己默认的登录页。但是这还不行,

当用户点击登录时,

1.它会拿到用户输入的用户名密码;

2.根据用户名通过UserDetailsService 的 loadUserByUsername(username)方法获得一个用户对象;

3.获得一个UserDetails 对象,获得内部的成员属性password;

4.通过PasswordEncoder 的 matchs(s1, s2) 方法对比用户的输入的密码和第3步的密码;

5.匹配成功;

所以我们要实现这三个接口的三个方法:

1.实现UserDetailsService ,可以选择同时实现用户的正常业务方法和UserDetailsService ;

例如:UserServiceImpl implement IUserService,UserDetailsService {}

2.实现UserDetails ,一般使用用户的实体类实现此接口。

其中有getUsername(), getPassword(), getAuthorities()为获取用户名,密码,权限。可根据个人情况实现。

3.实现PasswordEncoder ,spring security 提供了多个该接口的实现类,可百度和查看源码理解,也可以自己写。

三个实现类的配置如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private UserDetailsService userDetailsService; @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}

其中Userdetails 为UserDetailsService 中 loadUserByUsername() 方法的返回值类型。

到目前为止,就可以完成简单认证了。而授权管理,到现在,是默认的:所有资源都只有‘认证’权限,所有用户也只有‘认证’权限。即,经过认证就可以访问所有资源。

以上,就是spring security的简易应用。可以实现一个稍微完整的安全控制。非常简单。

授权管理:

授权管理,是在已认证的前提下。用户在认证后,根据用户的不同权限,开放不同的资源。

根据RBAC设计,用户有多个角色,角色有多个权限。(真正控制资源的是权限,角色只是一个权限列表,方便使用。)

每个用户都有一个权限列表,授权管理,就是权限和资源的映射。在编程中,写好对应关系。然后当用户请求资源时,查询用户是否有资源对应的权限决定是否通过。

权限写在数据库,配置文件或其他任何地方。只要调用loadUserByUsername()时返回的UserDetails对象中的getAuthorities()方法能获取到。

所以无论用户的权限写在哪里,只要getAuthorities()能得到就可以了。

举例:

授权管理映射:add==/api/add,query==/api/query;

数据库中存储了用户权限:query;

那么该用户就只能访问/api/query,而不能访问/api/add。

授权管理配置如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private UserDetailsService userDetailsService; @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add")
.antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query")
.antMatchers("/home").hasAuthority("base");
}
}

以上就是spring security的基本应用。下面是解决前后端分离下的无法302跳转的情况。

需求是:前后端分离,需要自己的登录页面,使用ajax请求。

出现问题:自己的登录页面请求登录后,后端返回302跳转主页,ajax无法处理;未认证请求资源时,后端返回302跳转登录页,也无法处理。

解决思想:修改302状态码,修改为401,403或者200和json数据。

HttpSecurity 有很多方法,可以看一看

比如 设置登录页(formLogin().loginPage("/login.html")) 可以设置自己的登录页(该设置主要是针对使用302跳转,且有自己的登录页,如果不使用302跳转,前后端完全分离,无需设置)。

比如 设置认证成功处理

比如 设置认证失败处理

比如 设置异常处理

比如 设置退出成功处理

可以继承重写其中的主要方法(里面有httpResponse对象,可以随便返回任何东西)

例如:

import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; @Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setStatus(HttpStatus.OK.value());
}
}

设置完成登录成功和失败处理后,还是不够满足需求,当用户未通过登录页进入网站,我们需要在用户直接访问资源时,告诉前端此用户未认证。(默认是302跳转到登录页)。我们可以改成返回403状态码。

这里就需要实现一个特殊的方法:AuthenticationEntryPoint 接口的 commence()方法。

这个方法主要是,用户未认证访问资源时,所做的处理。

spring security给我们提供了很多现成的AuthenticationEntryPoint 实现类,

比如默认的302跳转登录页,比如返回403状态码,还比如返回json数据等等。当然也可以自己写。和上面的登录处理一样,实现接口方法,将实现类实例传到配置方法(推荐spring注入)。

如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Qualifier("userService")
@Autowired
private UserDetailsService userDetailsService; @Autowired
private LoginSuccessHandler loginSuccessHandler; @Autowired
private LoginFailureHandler loginFailureHandler; @Autowired
private MyLogoutHandler logoutHandler; @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
} @Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginProcessingUrl("/login")
// 登录成功
.successHandler(loginSuccessHandler)
// 登录失败
.failureHandler(loginFailureHandler).permitAll()
.and()
// 注销成功
.logout().logoutSuccessHandler(logoutHandler)
.and()
// 未登录请求资源
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint())
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add")
.antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query")
.antMatchers("/home").hasAuthority("base");
}
}

以上就算是完了,前端发起ajax请求时,后端会返回200,401,403状态码,前端可根据状态码做相应的处理。

以下是我的全部代码(后端,安全管理demo)

https://github.com/Question7/spring-security-demo

spring security简单教程以及实现完全前后端分离的更多相关文章

  1. SpringMVC+Spring+mybatis+maven+搭建多模块框架前后端分离开发框架的完整demo,拿走不谢。——猿实战02

            猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是 ...

  2. 超简单!asp.net core前后端分离项目使用gitlab-ci持续集成到IIS

    现在好多使用gitlab-ci的持续集成的教程,大部分都是发布到linux系统上的,但是目前还是有很大一部分企业使用的都是windows系统使用IIS在部署.NET应用程序.这里写一下如何使用gitl ...

  3. 超简单工具puer——“低碳”的前后端分离开发

    本文由作者郑海波授权网易云社区发布. 前几天,跟一同事(MIHTool作者)讨教了一下开发调试工具.其实个人觉得相较于定制一个类似MIHTool的Hybrid App容器,基于长连的B/S架构的工具其 ...

  4. SpringBoot+Jpa+SpringSecurity+Redis+Vue的前后端分离开源系统

    项目简介: eladmin基于 Spring Boot 2.1.0 . Jpa. Spring Security.redis.Vue的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 ...

  5. Spring Security + JWT实现前后端分离权限认证

    现在国内前后端很多公司都在使用前后端分离的开发方式,虽然也有很多人并不赞同前后端分离,比如以下这篇博客就很有意思: https://www.aliyun.com/jiaocheng/650661.ht ...

  6. SpringBootSecurity学习(12)前后端分离版之简单登录

    前后端分离 前面讨论了springboot下security很多常用的功能,其它的功能建议参考官方文档学习.网页版登录的形式现在已经不是最流行的了,最流行的是前后端分离的登录方式,前端单独成为一个项目 ...

  7. 手把手教你使用 Spring Boot 3 开发上线一个前后端分离的生产级系统(一) - 介绍

    项目简介 novel 是一套基于时下最新 Java 技术栈 Spring Boot 3 + Vue 3 开发的前后端分离的学习型小说项目,配备详细的项目教程手把手教你从零开始开发上线一个生产级别的 J ...

  8. Docker环境下的前后端分离项目部署与运维

    本教程将从零开始部署一个前后端分离的开源项目,利用docker虚拟机的容器技术,采用分布式集群部署,将项目转换成为高性能.高负载.高可用的部署方案.包括了MySQL集群.Redis集群.负载均衡.双机 ...

  9. 用Spring Security, JWT, Vue实现一个前后端分离无状态认证Demo

    简介 完整代码 https://github.com/PuZhiweizuishuai/SpringSecurity-JWT-Vue-Deom 运行展示 后端 主要展示 Spring Security ...

随机推荐

  1. 记录面试一位三年经验Web前端开发者的过程

    今天是2019年6月5日,后天就是端午节了,提前祝端午节快乐! 好了,开始这次面试过程的正题部分. 当我从人事手中接下这份三年哥(暂拟名称)的简历的时候,看到三年工作经验,心想 这应该是个大佬了 挺厉 ...

  2. JavaScript中的垃圾收集机制

     JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存. 在编写 JavaScript 程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的 ...

  3. MySQL 简介

    MySQL 简介 点击查看MySQL官方网站 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,后来被Sun公司收购,Sun公司后来又被Oracle公司收购,目前属于Oracle旗 ...

  4. 好玩的Linux命令-1

    Ag:比grep.ack更快的归递搜索文件内容 1:首先在linux创建个sh文件->ag.sh 2:在ag.sh里面输入如下内容并保存 #!/bin/bash set -x TEMP_DIR= ...

  5. V7双雄-基于Virtex7XC7VX690T的高性能计算板卡解决方案

    北京太速V7双雄-基于Virtex7XC7VX690T的高性能计算板卡

  6. [Luogu1220]关路灯(区间dp)

    [Luogu1220]关路灯 题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏 ...

  7. spl_autoload_register() 函数实现的自动加载

    和Python用module来区分代码块不同,PHP按照命名空间来区分,开始学PHP的时候一心认定了如果想用 use 关键字来导入(Python的习惯说法)一个类或者函数或者其他对象的话,必须先inc ...

  8. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  9. Delphi GridPanel Percent百分比设置

    可能很多人都有这个困扰,为什么每次设置一个百分比后,值都会改变,只有设置成absolute​才会正常,经摸索发现,是因为精度引起,设置percent的时候,需要将精确到多个小数位.如要有3列,需要设置 ...

  10. 如何删除发布服务器distribution

    在建立发布服务器后自动生成distribution数据库为系统数据库,drop无法删除,实际删除方法如下:在“对象资源管理器”-“复制”上点击右键,选择“禁用发布和分发”,依次执行即可完成该系统数据库 ...