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 ...
随机推荐
- Python笔记_第五篇_Python数据分析基础教程_文件的读写
1. 读写文件(基本) savetxt.loadtxt i2 = np.eye(2) print(i2) np.savetxt(r"C:\Users\Thomas\Desktop\eye.t ...
- OSS 图片处理流程
1.步骤一 2.步骤二 3.步骤三 4.步骤四 5.步骤五(步骤4完成会自动添加cname用户解析,不需要自己去加,只需要点击进来看下是否添加成功即可) 通过以上步骤就可以实现了图片服务的配置
- SQL 2005 带自增列 带外键约束 数据导入导出
1,生成建表脚本 选中要导的表,点右键-编写表脚本为-create到 ,生成建表脚本 2,建表(在新库),但不建外键关系 不要选中生成外键的那部分代码,只选择建表的代码 3,导数据,用SQL STU ...
- Res-net 标准版本源码差异-官方源码示例
# resnet https://github.com/tensorflow/models/blob/master/research/slim/nets/resnet_v1.py https://gi ...
- GUI(Graphical User Interface)
译:用户和图形界面 GUI与程序交互的不同方式,包含3基本要素:输入,处理和输出. 常用GUI框架包括以下几种: wxPython Kivy Flexx PyQt Tkinter Pywin32 Py ...
- awk 中 RS,ORS,FS,OFS 区别与联系
一,RS与ORS 1,RS是记录分隔符,默认的分隔符是\n,具体用法看下 [root@krlcgcms01 mytest]# cat test1 //测试文件 111 222 333 444 ...
- awk使用笔记
awk特殊字符打印方法: 1.awk打印双引号: awk '{print "\""}' 2.awk打印单引号: awk '{print "'\''&quo ...
- Python笔记_第四篇_高阶编程_高阶函数_3.sorted
1. sorted函数: 常用的排序分:冒泡排序.选择排序.快速排序.插入排序.计数器排序 实例1:普通排序 # 普通排序 list1 = [,,,,] list2 = sorted(list1) # ...
- 【按位dp】1出现的次数
l-r1出现的次数 注意端点处理 垃圾算法书 垃圾代码毁我青春 自己研究写了写 #include <iostream> #include <string> #include & ...
- ZJNU 2135 - 小智的宝可梦
因为成环 所以可以枚举第1只与第n只喂的次数 然后第1只和第2只的次数就固定了,以此类推,则所有宝可梦喂的次数都固定了 最后处理完检查是否全为0,不是则进行下一次枚举,是则直接输出Yes 如果所有枚举 ...