Spring-security整理
出于某些原因,需要学习一下spring的安全框架。(研究半天,如果单单说用户认证和授权这块儿,我感觉还是shiro好用。)
spring security介绍可以参考一下以下文档:
(满满的羡慕啊)我这里就不扯了。
http://www.tianshouzhi.com/api/tutorials/spring_security_4/252
一、先贴代码
下边是我的项目结构
当然我采用的是springboot + thymeleaf + mybatis + mysql
1.用到的pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency> <!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency> <!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency> <!-- security+thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.5.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<!-- <version>2.1.3.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>
pom.xml
2.核心配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.MessageDigestPasswordEncoder; @Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService customUserService(){ //注册UserDetailsService 的bean
return new UserDetailsServiceImpl();
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()).passwordEncoder(new MessageDigestPasswordEncoder("MD5")); //user Details Service验证
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //authenticated任何请求,登录后可以访问
.and()
.csrf().disable()
.formLogin()
.loginPage("/user/login")//
.defaultSuccessUrl("/user/index")
.failureUrl("/user/login?error=true")
.permitAll() //登录页面用户任意访问
.and()
.logout().permitAll(); //注销行为任意访问
}
}
WebSecurityConfig.java
3.UserDetailsServiceImpl
import com.qx.demo.entity.RolePo;
import com.qx.demo.entity.UserPo;
import com.qx.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.HashSet;
import java.util.Set; public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserService service;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
UserPo user = service.getUserById(s);
if (user==null){
throw new UsernameNotFoundException("用户名不存在");
}
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
for (RolePo item:user.getRoles()){
authorities.add(new SimpleGrantedAuthority(item.getRName()));
System.out.println(item.getRName());
}
return new User(user.getUsername(),user.getPassword(),authorities);
}
}
UserDetailsServiceImpl.java
4.控制层Controller
import com.qx.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; @Controller
@RequestMapping("user")
public class UserController {
@Autowired
private UserService service;
@RequestMapping("login")
public ModelAndView toLogin(HttpServletRequest request){
ModelAndView mv= new ModelAndView("login");
String error = request.getParameter("error");
if (error!=null && error.equals("true")){
System.out.println("登录失败");
}
return mv;
}
@RequestMapping("index")
public String getUserById( Authentication authentication){
User principal = (User)authentication.getPrincipal();
if (authentication!=null)
System.out.println(authentication.getCredentials()+",\n"+authentication.getDetails()+",\n"+authentication.getPrincipal()+",\n"+authentication.getName());
return "index";
}
}
UserController.java
5.实体类
import lombok.Data;
import lombok.ToString; import java.util.List; @Data
@ToString
public class UserPo {
private int uid;
private String username;
private String password;
private List<RolePo> roles;
}
UserPo.java
import lombok.Data;
import lombok.ToString; import java.util.List; @Data
@ToString
public class RolePo {
private int rid;
private String rName;
private List<PermissionPo> pers;
}
RolePo.java
import lombok.Data;
import lombok.ToString; @Data
@ToString
public class PermissionPo {
private int pid;
private String pName;
private String pPer;
}
PermissionPo
6.Dao层
import com.qx.demo.entity.UserPo; public interface UserMapper {
UserPo getUserByUsername(String username);
}
UserMapper.java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qx.demo.dao.UserMapper"> <resultMap id="getRoleAndPer" type="com.qx.demo.entity.UserPo">
<id column="uid" property="uid"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<collection property="roles" ofType="com.qx.demo.entity.RolePo">
<id column="rid" property="rid"></id>
<result column="r_name" property="rName"></result>
<collection property="pers" ofType="com.qx.demo.entity.PermissionPo">
<id column="pid" property="pid"></id>
<result column="p_name" property="pName"></result>
<result column="p_per" property="pPer"></result>
</collection>
</collection>
</resultMap>
<select id="getUserByUsername" parameterType="String" resultMap="getRoleAndPer">
SELECT * FROM qx_user qu
INNER JOIN `qx_user_role` qur ON qu.`uid`=qur.`u_id`
INNER JOIN qx_role qr ON qur.`r_id` = qr.`rid`
INNER JOIN qx_role_per qrp ON qr.`rid`=qrp.`pid`
INNER JOIN qx_permission qp ON qrp.`pid`=qp.`pid`
WHERE username=#{_parameter}
</select>
</mapper>
Mapper.xml
7.service层
import com.qx.demo.entity.UserPo; public interface UserService {
UserPo getUserById(String id);
}
UserService.java
import com.qx.demo.dao.UserMapper;
import com.qx.demo.entity.UserPo;
import com.qx.demo.service.UserService;
import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper mapper;
@Override
public UserPo getUserById(String username) {
return mapper.getUserByUsername(username);
}
}
UserServiceImpl.java
8.application.properties
mybatis.mapper-locations=/mapper/*.xml
mybatis.type-aliases-package=com.qx.demo.entity spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///qx
spring.datasource.username=root
spring.datasource.data-password=root spring.thymeleaf.mode=HTML5
spring.thymeleaf.cache=false logging.level.com.qx.demo.dao=debug
application.properties
二、再来看看spring-security的大概的执行流程
1.从上边图不难看出,spring-security的核心应该就是ProviderManager,但是ProviderManager并不直接执行认证和授权等相关操作,而是委托给AuthenticationProvider去完成相关操作,而对于每一个ProviderManager都是可以有多个AuthenticationProvider的。
2.AuthenticationProvider首先会找到UserDetailsService(有我们自己定义的)在数据库中查找用户信息。如果没有找到将会返回一个空的UserDetails,由于UserDetails是一个接口,我是在实体类中实现了该接口(那么这样我们就可以返回一个空壳user对象了)。
当然,如果找到了我们可以直接授权并将UserDetailsService返回。
3.AuthenticationProvider就收到一个非空的UserDetailsService,接下来就该进行密码比对了。这时AuthenticationProvider将会调用PasswordEncoder进行密码比对。
4.无论成功与否,spring-security都会在对应的过滤器上找到响应的响应方式,并响应给客户。
Spring-security和shiro一样,都是基于过滤器,所以我们免不了去配置一些过滤器:
当然,在前边的代码中已经体现过,该配置应该是在spring-security的核心配置类中进行配置(如果是xml也是一样的,大同小异嘛)。
上图是基于前后端分离,给前端返回一个json格式响应体的,比如登录成功时:
如图所示我们只需要实现AuthenticationSuccessHandler接口,并重写onAuthenticationSuccess方法即可。这个方法体完全就是一个类似与Servlet的方法体,有一定javaee基础的应该都没啥问题的。同样的道理如果登陆失败我们可以实现AuthenticationFailureHandler等等。
嗯,可能我忘了记录一个至关重要的玩意了,没错就是他:
你没看错,这就是一个基于用户名和密码的filter,spring-security拼什么这么智能,其实很简单就是一系列过滤器的使用。在源码中我们不难看出spring-security是设置了默认的登录地址的 /login,并且请求只允许POST请求。同时他也是spring-security默认登录过滤器。由于Java的开放原则的原因,你要是看他不爽你也可以自定义一个过滤器,你只需要继承一下AbstractAuthenticationProcessingFilter,重写其中的一些方法就好了。一般情况下我觉得默认的就可以了。
同样的与shiro雷同UsernamePasswordAuthenticationToken就是一个主体,当然保存的是客户端输入的登录信息。他最终是与UserDetailsService(在数据库中查到的信息)相对比得到登录结果。
对于用户信息来说我想密码应该是最重要的了,那么我们现在可以看看PasswordEncoder了
以上是security5中所有的PasswordEncoder了,当然,上边截图中有一个是我重写的PasswordEncoder(MyPasswordEncoder是我重写的)。这里我就不解释这些密码比对器都是什么作用了,如果有不了解的大家可以自己下去查询一下资料。我们公司要求的用的是MD5加密的方式,由于它是普通的散列,上网一百度就能看到答案,所以我需要重写一下PasswordEncoder。
同样的我们需要实现这个接口,我们需要重写两个方法encode()、matches()。一眼看上去不难发现,matches一定是用户认证的,那我们就可以把重点放到这个方法下了。
因为是例子,所以我写的比较简单了,显而易见我用的Spring自带的MD5加密工具了。
当然,spring-security是比较推荐 BCryptPasswordEncoder,我也象征性的看了一下源码并不是很难理解大家可以去看下。
Spring-security整理的更多相关文章
- Spring Security OAuth2 授权失败(401) 问题整理
Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考 http://www.ruanyifeng.com/blog/2014/0 ...
- 【OAuth2.0】Spring Security OAuth2.0篇之初识
不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...
- Spring Security 从配置入门 学习讲解。万恶之源------------web.xml
这段时间,工作闲了下来,接触了Spring Security,对于我一个基础很差的人来说,无疑是个挑战啊. 经过一段时间的摸索,终于有了点眉目,在这里,要特别感谢http://blog.csdn.ne ...
- 使用Spring Security Oauth2完成RESTful服务password认证的过程
摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...
- spring security之httpSecurity使用示例
如果在HttpSecurity中配置需要authenticate(),则如果没有登陆,或没有相关权限,则会无法访问 2017-01-02 23:39:32.027 DEBUG 10396 --- [n ...
- spring security执行流程图
今天看到非常多人转载了这篇文章,这里备注一下.原文来自CSDN我的博客. 近期在研究spring security的配置,研究了一个星期了,在官网看了下.仅仅配置出来了简单的登录,但不知如何从数据库读 ...
- Spring Security 集成 CAS(基于HTTP协议版本)
Spring Security 集成 CAS(基于HTTP协议版本) 近段时间一直研究Spring Security 集成 CAS,网上资料相关资料也很多,不过大都是基于Https的安全认证;使用ht ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- SpringBoot + Spring Security 学习笔记(一)自定义基本使用及个性化登录配置
官方文档参考,5.1.2 中文参考文档,4.1 中文参考文档,4.1 官方文档中文翻译与源码解读 SpringSecurity 核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) ...
- 256.Spring Boot+Spring Security: MD5是加密算法吗?
说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)h ...
随机推荐
- h5-web字体和字体图标
想要使用可以在: https://www.iconfont.cn/webfont?spm=a313x.7781069.1998910419.d81ec59f2#!/webfont/index :是we ...
- 【JavaScript】回流(reflow)与重绘(repaint)
重绘与回流 首先要了解页面是如何呈现的: HTML文档加载后生成DOM树(包括display:none;元素): 在DOM树的基础上配合css样式结构体生成render树(不包含display:non ...
- 目录服务不能与此服务器复制,因为距上一次与此服务器复制的时间已经超过了 tombstone 生存时间。
1.PDC数据正常 2.其他DC无法复制,报如下错误:目录服务不能与此服务器复制,因为距上一次与此服务器复制的时间已经超过了 tombstone 生存时间. 3.直接强制复制即可 repadmin / ...
- 吴裕雄--天生自然MySQL学习笔记:MySQL 复制表
如果需要完全的复制MySQL的数据表,包括表的结构,索引,默认值等. 如果仅仅使用CREATE TABLE ... SELECT 命令,是无法实现的. 如何完整的复制MySQL数据表,步骤如下: 使用 ...
- Mybatis之二级缓存(八)
1. 介绍 Mybatis缓存分为一级缓存和二级缓存,在本节中我们介绍下二级缓存的使用及其特性 MyBatis的一级缓存是在一个Session域内有效的,当Session关闭后,缓存内容也随之销毁.但 ...
- 洛谷 P1341 无序字母对(欧拉回路)
题目传送门 解题思路: 一道欧拉回路的模板题,详细定理见大佬博客,任意门 AC代码: #include<cstdio> #include<iostream> using nam ...
- [NOI2017]蚯蚓排队(链表+hash)
这题看题面感觉挺玄学的,但其实会挂链式hash就能暴力切了,就是纸老虎,考察选手的语文水平.不过三年没写挂链hash也应该写一下了…… 首先模数设成自然溢出ull,然后挂链时的模数取2^24.然后就可 ...
- 杂记 -- 关于less在vue项目中的使用
1.安装less,less-loader npm install less less-loader --save 2.配置wepack.js(vue3+版本中不用自己设置) //添加less路径模块 ...
- UVA 10269 Super Mario,最短路+动态规划
这个题目我昨晚看到的,没什么思路,因为马里奥有boot加速器,只要中间没有城堡,即可不耗时间和脚力,瞬间移动不超过L距离,遇见城堡就要停下来,当然不能该使用超过K次...我纠结了很久,最终觉得还是只能 ...
- EL表达式和JSTL(二)——BeanUtils工具
BeanUtils工具 大对数人习惯使用JavaBean的get和set方法来获取和设置JavaBean的属性,但是在Java EE编程的过程中,会经常从配置文件中读取数据,但是从配置文件中读取的数据 ...