一、Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

  二、security和springboot也做了深度的契合,所以我们这里使用security来配置相关访问。因为项目可以做前后端的分理,我这里就不讲对于后台处理页面的配置了。这里主要是讲一些针对于纯后端开发,进行security的相关配置,当然只是其中一部分。

  三、讲到的点主要有:跨域、认证、加密、权限控制等。

  四、实现部分

  1、pom.xml需要的依赖(这里只写主要部分,parent等自己按需要导入依赖)

     <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

  说明:说明一点,我这里使用的是2.0.0的版本,如何有需要可以自己根据不同的版本进行配置

  2、主要的配置部分

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.web.filter.CorsFilter; @Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { //跨域
@Autowired
private CorsFilter corsFilter; //认证处理类
@Autowired
private DaoAuthenticationProvider daoAuthenticationProvider; //认证成功
@Autowired
private AuthenticationSuccessHandler successHandler; //认证失败
@Autowired
private AuthenticationFailureHandler failureHandler; //登出成功
@Autowired
private LogoutSuccessHandler logoutSuccessHandler; @Autowired
private AccessDeniedHandler deniedHandler; //认证EntryPoint
@Autowired
private AuthenticationEntryPoint entryPoint; @Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.authenticationProvider(daoAuthenticationProvider);
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/api/**")
.antMatchers("/swagger-ui.html")
.antMatchers("/webjars/**")
.antMatchers("/swagger-resources/**")
.antMatchers("/v2/**");
} @Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterBefore(corsFilter, CsrfFilter.class)
.exceptionHandling()
.authenticationEntryPoint(entryPoint)
.accessDeniedHandler(deniedHandler)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/api/user/login")
.successHandler(successHandler)
.failureHandler(failureHandler)
.and()
.logout().logoutUrl("/api/user/logout")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement().maximumSessions(1800);
}
}

  3、csrf防护

  这个我这里不详细讲,主要的目的就是每次访问的时候除了带上自己的访问凭据以外,还需要带上每次csrf的票据。当然这个是会根据具体的会话进行变化的,也就是防止csrf攻击。

  如果csrf放开配置方式可以为cookie

  即:将.csrf().disable()换成.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())

  如果存在后端接口忽略的加入:.ignoringAntMatchers("/api/user/login")

  访问的时候带上csrf的访问票据,携带方式为下面2种方式。票据的获取方式为第一次访问的时候回通过cookie的方式带入

  request:_csrf:票据

  header:X-XSRF-TOKEN:票据

  4、跨域(配置方式见注入部分)

  跨域问题我相信在使用前后台分理的时候肯定会出现这种问题,那么怎么样配置跨域问题呢!这里提供了一种方式(CorsFilter.class)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import java.util.ArrayList;
import java.util.List; @Configuration
public class CorsFilterConfiguration { @Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
List<String> allowedOrigins = new ArrayList<>();
allowedOrigins.add("*");
List<String> allowedMethods = new ArrayList<>();
allowedMethods.add("*");
List<String> allowedHeaders = new ArrayList<>();
allowedHeaders.add("*");
List<String> exposedHeaders = new ArrayList<>();
exposedHeaders.add("Link");
exposedHeaders.add("X-Total-Count");
corsConfiguration.setAllowedOrigins(allowedOrigins);
corsConfiguration.setAllowedMethods(allowedMethods);
corsConfiguration.setAllowedHeaders(allowedHeaders);
corsConfiguration.setExposedHeaders(exposedHeaders);
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setMaxAge(1800L);
source.registerCorsConfiguration("/api/**", corsConfiguration);return new CorsFilter(source);
}
}

  5、认证处理以及加密处理

  这里的加密方式采用的是springsecurity提供的一种加密方式:BCryptPasswordEncoder(hash、同一密码加密不一样的密钥),认证配置见builder部分

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration
public class PasswordEncoderConfiguration { /**
* 密码加密
*/
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
import com.cetc.domain.Role;
import com.cetc.domain.User;
import com.cetc.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList;
import java.util.List; @Service
@Transactional
public class AuthDetailsService implements UserDetailsService { @Autowired
private UserRepository userRepository; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("用户不存在!");
}
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
List<Role> roles = user.getRoles();
if (roles != null && !roles.isEmpty()) {
roles.stream().forEach(role -> simpleGrantedAuthorities.add(new SimpleGrantedAuthority(role.getRoleType())));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), simpleGrantedAuthorities);
}
}

  说明:这里的UsernameNotFoundException如果是默认配置,是不能被处理类所捕获的。原因:DaoAuthenticationProvider父类AbstractUserDetailsAuthenticationProviderhideUserNotFoundExceptionstrue

  解决方式重新配置:DaoAuthenticationProvider 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration
public class CustomDaoAuthenticationProvider { @Autowired
private AuthDetailsService authDetailsService; @Autowired
private BCryptPasswordEncoder passwordEncoder; @Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(authDetailsService);
provider.setPasswordEncoder(passwordEncoder);
provider.setHideUserNotFoundExceptions(false);
return provider;
}
}

  然后修改builder.userDetailsService(authDetailsService).passwordEncoder(passwordEncoder);builder.authenticationProvider(provider);

  这种方式就可以解决异常包装的问题了,这里我们是采用的原生的配置方式。

  6、各个切入点(AuthenticationEntryPoint、AccessDeniedHandler、AuthenticationSuccessHandler、AuthenticationFailureHandler、LogoutSuccessHandler)五个切入点,作用就是在对应操作过后,可以根据具体的切入点进行相应异常的处理

import com.alibaba.fastjson.JSONObject;
import com.cetc.constant.SystemErrorCode;
import com.cetc.dto.MenuDTO;
import com.cetc.result.ResponseMsg;
import com.cetc.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import java.util.List; @Configuration
public class CustomHandlerConfiguration { @Autowired
private IUserService userService; /**
* 访问接入点处理
* @return
*/
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
AuthenticationEntryPoint entryPoint = (request, response, e) -> {
ResponseMsg<String> responseMsg = new ResponseMsg<>();
responseMsg.setStatus(false);
responseMsg.setBody(e.getMessage());
responseMsg.setErrorCode(SystemErrorCode.NO_LOGIN);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSONObject.toJSONString(responseMsg));
};
return entryPoint;
} /**
* 接入过后问题处理
* @return
*/
@Bean
public AccessDeniedHandler accessDeniedHandler() {
AccessDeniedHandler accessDeniedHandler = (request, response, e) -> {
ResponseMsg<String> responseMsg = new ResponseMsg<>();
responseMsg.setStatus(false);
responseMsg.setBody(e.getMessage());
responseMsg.setErrorCode(SystemErrorCode.NO_PERMISSION);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSONObject.toJSONString(responseMsg));
};
return accessDeniedHandler;
} /**
* 登录成功后的处理
* @return
*/
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
AuthenticationSuccessHandler authenticationSuccessHandler = (request, response, authentication) -> {
//返回数据
ResponseMsg<List<MenuDTO>> responseMsg = new ResponseMsg<>();
responseMsg.setBody(userService.getMenus());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSONObject.toJSONString(responseMsg));
};
return authenticationSuccessHandler;
} /**
* 登录失败后的处理
* @return
*/
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
AuthenticationFailureHandler authenticationFailureHandler = (request, response, e) -> {
ResponseMsg<String> responseMsg = new ResponseMsg<>();
responseMsg.setStatus(false);
responseMsg.setBody(e.getMessage());
responseMsg.setErrorCode(SystemErrorCode.ACCOUNT_ERROR);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSONObject.toJSONString(responseMsg));
};
return authenticationFailureHandler;
} /**
* 登出成功后的处理
* @return
*/
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
LogoutSuccessHandler logoutSuccessHandler = (request, response, authentication) -> {
ResponseMsg<String> responseMsg = new ResponseMsg<>();
responseMsg.setStatus(true);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSONObject.toJSONString(responseMsg));
};
return logoutSuccessHandler;
}
}

  其他的就不详细介绍了,基本上都是怎么样去处理,在具体的接入点出现的问题。

  7、登录、登出

  登录默认的参数为username、password 采用表单方式提交。如果需要修改参数名称可以在loginPage后面加入

.usernameParameter("name")
.passwordParameter("pwd")

  说明:默认登录、登出配置的接口不需要实现,默认也是放开的。

  8、无需验证访问

  在自己开发接口的时候肯定不需要进行权限的访问,这个时候就可以通过配置方式放开具体的请求在.authorizeRequests()配置

.antMatchers("/api/user/register").permitAll()

  9、默认会话超时30分钟,可以通过配置修改会话保存时间

server:
servlet:
session:
timeout: 1800s

springboot对security的后端配置的更多相关文章

  1. SpringBoot + Spring Security 学习笔记(一)自定义基本使用及个性化登录配置

    官方文档参考,5.1.2 中文参考文档,4.1 中文参考文档,4.1 官方文档中文翻译与源码解读 SpringSecurity 核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) ...

  2. spring boot + spring security +前后端分离【跨域】配置 + ajax的json传输数据

    1.前言 网上各个社区的博客参差不齐 ,给初学者很大的困扰 , 我琢磨了一天一夜,到各个社区找资料,然后不断测试,遇到各种坑,一言难尽啊,要么源码只有一部分,要么直接报错... 最后实在不行,直接去看 ...

  3. AgileBoot - 基于SpringBoot + Vue3的前后端快速开发脚手架

    AgileBoot 仓库 后端地址:https://github.com/valarchie/AgileBoot-Back-End 技术栈:Springboot / Spring Security / ...

  4. SpringBoot + Spring Security 学习笔记(五)实现短信验证码+登录功能

    在 Spring Security 中基于表单的认证模式,默认就是密码帐号登录认证,那么对于短信验证码+登录的方式,Spring Security 没有现成的接口可以使用,所以需要自己的封装一个类似的 ...

  5. SpringBoot + Spring Security 学习笔记(三)实现图片验证码认证

    整体实现逻辑 前端在登录页面时,自动从后台获取最新的验证码图片 服务器接收获取生成验证码请求,生成验证码和对应的图片,图片响应回前端,验证码保存一份到服务器的 session 中 前端用户登录时携带当 ...

  6. 基于Springboot集成security、oauth2实现认证鉴权、资源管理

    1.Oauth2简介 OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAu ...

  7. springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

    项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖  ...

  8. SpringBoot中使用UEditor基本配置(图文详解)

    SpringBoot中使用UEditor基本配置(图文详解) 2018年03月12日 10:52:32 BigPotR 阅读数:4497   最近因工作需要,在自己研究百度的富文本编辑器UEditor ...

  9. SpringBoot:配置文件及自动配置原理

    西部开源-秦疆老师:基于SpringBoot 2.1.6 的博客教程 秦老师交流Q群号: 664386224 未授权禁止转载!编辑不易 , 转发请注明出处!防君子不防小人,共勉! SpringBoot ...

随机推荐

  1. Django视图(一)

    Django视图(一) 一. 概述 作用:视图接受web请求,并相应请求 本质:视图是自定义的一个python中的函数 响应内容:正常视图,重定向视图,错误视图(404,500,400) 响应过程: ...

  2. js事件的机制

    1.html事件处理程序 <button id="btn1" onclick="alert(1);">按钮1</button> 2.do ...

  3. JavaScript编写学生查询系统

    const readline = require('readline-sync')//引用readline-sync //用户名,密码 let user = [{ username: 'yang', ...

  4. c#采用emit将DataTable转List

    前面已经说了List转DataTable,也整理了代码. 现在转回来说说DataTable转List. 先举一个例子 public class Person { public int Age{get; ...

  5. 复习Vue

    以前学过vue,但是工作中一直没有用到都忘记了最近在复习下正好做个笔记偶尔看看,(目前常更新,2018年6月25日) 1.指令 setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. ...

  6. javascript的执行机制—Event Loop

    既然今天要谈的是javascript的事件循环机制,要理解事件循环,首先要知道事件循环是什么. 我们先从一个例子来看一下javascript的执行顺序. <script> setTimeo ...

  7. c++友元函数、友元类、友成员函数

    友元函数:不是类成员函数,是一个类外的函数,但是可以访问类所有成员. class Point{ public: friend void fun(Point t);//友元函数 private: int ...

  8. C++编译器是如何管理类和对象的,类的成员函数和成员变量

    C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类.从计算机的角度,程序依然由数据段(栈区内存)和代码段(代码区内存)构成. #include ...

  9. Nginx 解决504 Error 最简单的方法

    <?php // Where am I ? set_time_limit(0); 就这个多,当然了,服务器还是务求稳妥,应当结合服务器和业务需求从配置上优化,才是正理.

  10. linux 相关命令记录

    NetworkManager关闭及禁用 关闭:systemctl stop NetworkManager 禁用:systemctl disable NetworkManager 查看日志:journa ...