1 环境搭建

  1.1 环境说明

    JDK:1.8

    MAVEN:3.5

    SpringBoot:2.0.4

    SpringSecurity:5.0.7

    IDEA:2017.02旗舰版

  1.2 环境搭建

    创建一个SpringBoot项目,引入相关依赖:WEB、JPA、MYSQL、SpringSecurity、lombok、devtools

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.xunyji</groupId>
<artifactId>springsecurity03</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>springsecurity03</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

pom.xml

  1.3 MySQL连接信息配置

    在yml配置文件中配置MySQL相关的配置,参考文档

spring:
datasource:
url: jdbc:mysql://127.0.0.1/testdemo?characterEncoding=utf-8&useSSL=false
username: root
password: 182838 jpa:
properties:
hibernate:
format_sql: true
show_sql: true

application.yml

  1.4 创建一个Restful接口

    该接口主要用于测试用的

package com.xunyji.springsecurity03.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @author 王杨帅
* @create 2018-09-08 21:51
* @desc
**/
@RestController
@RequestMapping(value = "/test")
@Slf4j
public class TestController { @GetMapping(value = "/home")
public String home() {
String info = "寻渝记主页面";
log.info(info);
return info;
} }

TestController.java

  1.5 启动应用

    技巧01:SpringBoot集成了SpringSecurity后,默认会对除了 /login 的所有请求进行拦截认证,所以我们启动应用后访问 /test/home 时会自动重定向到 /login 去进行登录录入

    技巧02:SpringSecurity默认提供了一个用户,用户名是 user, 用户密码在启动应用时会打印输出到控制台

    技巧03:SpringSecurity默认在登录成功后会自动跳转到之前访问的请求【PS: 这种方式在前后端分离的项目中并不适用,所以需要进行登录成功和失败来接,让登录成功和失败后返回JSON信息给前端,具体怎么跳转有前端进行控制】

2 基于内存的认证和授权

  待更新......2018年9月8日22:01:11

  参考文档

3 基于JPA实现SpringSecurity的认证和授权

  3.1 创建数据库表

    用户表:用于存放用户的相关信息

    角色表:用于存放角色信息【PS: 角色表插入数据时必须全部大写,而且要以 ROLE_ 开头】

    用户角色表:用户存放用户和角色的关联信息【PS: 用户和角色是多对多的关系】

/*
Navicat MySQL Data Transfer Source Server : mysql5.4
Source Server Version : 50540
Source Host : localhost:3306
Source Database : testdemo Target Server Type : MYSQL
Target Server Version : 50540
File Encoding : 65001 Date: 2018-09-08 21:30:53
*/ SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for `security_authority`
-- ----------------------------
DROP TABLE IF EXISTS `security_authority`;
CREATE TABLE `security_authority` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`authority` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; -- ----------------------------
-- Records of security_authority
-- ----------------------------
INSERT INTO `security_authority` VALUES ('', 'ROLE_ADMIN');
INSERT INTO `security_authority` VALUES ('', 'ROLE_USER');
INSERT INTO `security_authority` VALUES ('', 'ROLE_BOSS'); -- ----------------------------
-- Table structure for `security_user`
-- ----------------------------
DROP TABLE IF EXISTS `security_user`;
CREATE TABLE `security_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; -- ----------------------------
-- Records of security_user
-- ----------------------------
INSERT INTO `security_user` VALUES ('', 'admin', '$2a$10$AC0nr/qvRHHn2YNsKNbH/.X9f0dHhIZX0457mwFPBJ6jS2/Tcmu3S', '412444321@qq.com');
INSERT INTO `security_user` VALUES ('', 'wys', '$2a$10$YH9HijmebwcDfTdbx5ho2OlJ6.zewxufvCrnioVGI5PcXFsqNtCd6', '41fasd321@qq.com');
INSERT INTO `security_user` VALUES ('', 'boss', '$2a$10$iZ/467THEoA7E/MjOA6iJeBpZJpebIfRzvFbZhKNKwyyFfBypmQTi', 'asdfa@qq.com'); -- ----------------------------
-- Table structure for `security_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `security_user_role`;
CREATE TABLE `security_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4; -- ----------------------------
-- Records of security_user_role
-- ----------------------------
INSERT INTO `security_user_role` VALUES ('', '', '');
INSERT INTO `security_user_role` VALUES ('', '', '');
INSERT INTO `security_user_role` VALUES ('', '', '');
INSERT INTO `security_user_role` VALUES ('', '', '');
INSERT INTO `security_user_role` VALUES ('', '', '');
INSERT INTO `security_user_role` VALUES ('', '', '');

user_role.sql

  3.2 创建实体类

    技巧01:利用IDEA批量创建实体类,参考文档

  3.4 创建对应的Repository

    每个实体类创建一个Repository

    技巧01:利用Repository添加用户,添加用户时必须利用PasswordEncoder对密码进行加密

    技巧02:添加用户前需要创建一个PasswordEncoder的Bean

  3.5  创建服务层

    技巧01:本博文只创建了UserService的服务层,因为仅仅是实现认证和授权的功能

    技巧02:如果需要实现自定义的认证逻辑,就必须让UserService实现UserDetailsService接口,所有认证相关的逻辑都在loadUserByUsername方法中进行

      技巧03:loadUserByUsername方法中主要是根据用户名查找用户信息以及该用户的角色信息,该方法的返回值是SpringSecurity提供的User对象

package com.xunyji.springsecurity02.service;

import com.xunyji.springsecurity02.pojo.dataobject.SecurityAuthorityDO;
import com.xunyji.springsecurity02.pojo.dataobject.SecurityUserDO;
import com.xunyji.springsecurity02.pojo.dataobject.SecurityUserRoleDO;
import com.xunyji.springsecurity02.repository.AuthorityRepository;
import com.xunyji.springsecurity02.repository.UserRepository;
import com.xunyji.springsecurity02.repository.UserRoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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 java.util.List;
import java.util.stream.Collectors; /**
* @author 王杨帅
* @create 2018-09-08 20:46
* @desc
**/
@Service
public class UserService implements UserDetailsService { @Autowired
private UserRepository userRepository; @Autowired
private UserRoleRepository userRoleRepository; @Autowired
private AuthorityRepository authorityRepository; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 根据用户名查找用户信息
SecurityUserDO byUsername = userRepository.findByUsername(username);
System.out.println(byUsername); // 根据用户ID查找用户角色关联信息
List<SecurityUserRoleDO> byUserId = userRoleRepository.findByUserId(byUsername.getId());
byUserId.stream().forEach(System.out::println); // 获取该用户对应的所有角色的ID列表
List<Integer> collect = byUserId.stream()
.map(info -> info.getRoleId())
.collect(Collectors.toList());
collect.stream().forEach(System.out::println); // 获取该用户对应的所有角色信息
List<SecurityAuthorityDO> allById = authorityRepository.findAllById(collect);
allById.stream().forEach(System.out::println); List<SimpleGrantedAuthority> authorityList = allById.stream()
.map(info -> new SimpleGrantedAuthority(info.getAuthority()))
.collect(Collectors.toList());
authorityList.stream().forEach(System.out::println); // 封装该用户的用户名、密码、角色
return new User(byUsername.getUsername(), byUsername.getPassword(), authorityList);
}
}

UserService.java

  3.6 SpringSecurity相关配置

    3.6.1 认证成功拦截器

      认证成功后默认是跳转到之前的请求API,可以通过认证成功拦截器让认证成功后返回一些JSON信息

package com.xunyji.springsecurity02.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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; /**
* @author 王杨帅
* @create 2018-09-08 15:19
* @desc
**/
@Component
@Slf4j
public class XiangXuAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
log.info("登录成功");
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.getWriter()
.write(objectMapper.writeValueAsString(authentication));
}
}

XiangXuAuthenticationSuccessHandler.java

    3.6.2 认证失败拦截器

      认证失败后默认是跳转到 /login ,可以通过认证失败拦截器让认证失败后返回一些JSON信息

package com.xunyji.springsecurity02.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; /**
* @author 王杨帅
* @create 2018-09-08 15:22
* @desc
**/
@Component
@Slf4j
public class XiangXuAuthenticationFailureHandler implements AuthenticationFailureHandler { @Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
log.info("登陆失败"); response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception));
}
}

XiangXuAuthenticationFailureHandler.java

    3.6.2 自定义认证授权配置

package com.xunyji.springsecurity02.config;

import com.xunyji.springsecurity02.service.UserService;
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; /**
* @author 王杨帅
* @create 2018-09-08 20:13
* @desc
**/
@Configuration
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private PasswordEncoder passwordEncoder; @Autowired
private UserService userService; @Autowired
private AuthenticationFailureHandler xiangXuAuthenticationFailureHandler; @Autowired
private AuthenticationSuccessHandler xiangXuAuthenticationSuccessHandler; @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider()); // 添加自定义的认证逻辑
} @Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
} @Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginProcessingUrl("/furyLogin") // 提交登录信息的API
.usernameParameter("name") // 登录名
.passwordParameter("pwd") // 登录密码
.successHandler(xiangXuAuthenticationSuccessHandler) // 登录成功处理器
.failureHandler(xiangXuAuthenticationFailureHandler) // 登录失败处理器
.and().authorizeRequests()
.antMatchers("/test/home").permitAll() // 该api不需要授权
.anyRequest().authenticated() // 剩余都需要授权
.and()
.logout().permitAll() // 登出API不需要授权
.and()
.csrf().disable(); } /**
* 創建PsswordEncoder對應的Bean
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} /**
* 创建认证提供者Bean
* 技巧01:DaoAuthenticationProvider是SpringSecurity提供的AuthenticationProvider实现类
* @return
*/
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider(); // 创建DaoAuthenticationProvider实例
authProvider.setUserDetailsService(userService); // 将自定义的认证逻辑添加到DaoAuthenticationProvider
authProvider.setPasswordEncoder(passwordEncoder); // 设置自定义的密码加密
return authProvider;
} }

MySpringSecurityConfig.java

  3,7 编写控制类

package com.xunyji.springsecurity02.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @author 王杨帅
* @create 2018-09-08 21:17
* @desc
**/
@RestController
@RequestMapping(value = "/test")
@Slf4j
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启授权
public class TestController { @GetMapping(value = "/home")
public String home() {
String info = "寻渝记主页面";
log.info(info);
return info;
} @PreAuthorize("hasRole('ROLE_BOSS')") // 拥有ROLE_BOSS角色的用户可以访问
@GetMapping(value = "/boss")
public String boss() {
String info = "拥有ROLE_BOSS权限的才可以进入";
log.info(info);
return info;
} @PreAuthorize("hasRole('ROLE_BOSS') OR hasRole('ROLE_ADMIN')")
@GetMapping(value = "/admin")
public String admin() {
String info = "拥有ROLE_ADMIN权限的才可以进入";
log.info(info);
return info;
} @PreAuthorize("hasRole('ROLE_BOSS') OR hasRole('ROLE_ADMIN') OR hasRole('ROLE_USER')")
@GetMapping(value = "/user")
public String user() {
String info = "拥有ROLE_USER权限的才可以进入";
log.info(info);
return info;
} }

TestController.java

  3.8 启动测试

    技巧01:如果没有进行认证前访问需要进行权限的 Restful 服务,就会自动返回一个登录页面【问题:如何返回一个JSON信息呢】

    技巧02:登录认证成功后,如果访问该用户权限之外的 Restful 服务,那么就会提示权限不足【即:403状态码】

4 本博文源代码

  点击获取

SpringSecurity04 利用JPA和SpringSecurity实现前后端分离的认证和授权的更多相关文章

  1. springSecurity + jwt + redis 前后端分离用户认证和授权

    记录一下使用springSecurity搭建用户认证和授权的代码... 技术栈使用springSecurity + redis + JWT + mybatisPlus 部分代码来自:https://b ...

  2. 利用grunt-contrib-connect和grunt-connect-proxy搭建前后端分离的开发环境

    前后端分离这个词一点都不新鲜,完全的前后端分离在岗位协作方面,前端不写任何后台,后台不写任何页面,双方通过接口传递数据完成软件的各个功能实现.此种情况下,前后端的项目都独立开发和独立部署,在开发期间有 ...

  3. 【SpringSecurity系列2】基于SpringSecurity实现前后端分离无状态Rest API的权限控制原理分析

    源码传送门: https://github.com/ningzuoxin/zxning-springsecurity-demos/tree/master/01-springsecurity-state ...

  4. SpringBoot20 集成SpringSecurity02 -> 利用SpringSecurity进行前后端分离的登录验证

    1 SpirngBoot环境搭建 创建一个SpringBoot项目即可,详情参见三少的相关博文 参考博文 -> 点击前往 SpirngBoot项目脚手架 -> 点击前往 2 引入Spirn ...

  5. Spring-Gateway与Spring-Security在前后端分离项目中的实践

    前言 网上貌似webflux这一套的SpringSecurity操作资料貌似很少. 自己研究了一波,记录下来做一点备忘,如果能帮到也在迷惑的人一点点,就更好了. 新项目是前后端分离的项目,前台vue, ...

  6. 【SpringSecurity系列3】基于Spring Webflux集成SpringSecurity实现前后端分离无状态Rest API的权限控制

    源码传送门: https://github.com/ningzuoxin/zxning-springsecurity-demos/tree/master/02-springsecurity-state ...

  7. 【SpringSecurity系列1】基于SpringSecurity实现前后端分离无状态Rest API的权限控制

    源码传送门: https://github.com/ningzuoxin/zxning-springsecurity-demos/tree/master/01-springsecurity-state ...

  8. 如何利用vue和php做前后端分离开发?

    新手上路,前端工程师,刚毕业参加工作两个月,上面让我用vue搭建环境和php工程师一起开发,做前后端分离,然而我只用过简单的vue做一些小组件的经验,完全不知道怎样和php工程师配合,ps: php那 ...

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

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

随机推荐

  1. 3.07课·········if分支语句

    语句分类:顺序语句,选择语句(分支语句),循环语句 分支语句:(一)if(表达式) //表达式返回值是True或False{}说明:1.表达式返回的是bool值:2.小括号和花括号后面不需要加分号. ...

  2. JavaScript日期选择控件Kalendae

    在线演示 本地下载

  3. CSS3图片悬停放大动画

    在线演示 本地下载

  4. web框架详解之 tornado 四 模板引擎、session、验证码、xss

    一.模板引擎 基本使用 继承,extends 页面整体布局用继承 导入,include 如果是小组件等重复的那么就用导入 下面是目录 首先在controllers里面创建一个文件,文件里面是页面类 # ...

  5. 基于socket实现上传文件

    基于socket实现文件上传 客户端代码: #!/usr/bin/env python # -*- coding:utf-8 -*- """ 这个是实现上传文件 首先让客 ...

  6. Spring 相关

    1. spring的bean的scope属性范围  参考:http://jiangshuiy.iteye.com/blog/1667316 原理分析(bean的scope属性范围) scope用来声明 ...

  7. Struts基本原理 + 实现简单登录(二)

    MVC 概念 MVC全名是Model View Controller,是模型(model)—视图(view)—控制器(controller)的缩写,知道这么多就够了. 大家都知道SUN公司对于MVC模 ...

  8. intel dpdk api interrupt module 中断模块介绍

    声明:此文档只做学习交流使用,请勿用作其他商业用途 author:朝阳_tonyE-mail : linzhaolover@gmail.comCreate Date: 2013-7-12 11:46: ...

  9. Linux下c++11多线程聊天室

    刚看的c++11多线程,写个聊天室试试编译的时候加上 c++11 和 多线程库g++ -Wall -std=c++0x -pthread -o server server.cppserver 和cli ...

  10. QuickReport FastReport

    一.QuickReport1.安装Component->Install packages->X:/Program Files/Borland/Delphi7/Bin/dclqrt70.bp ...