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 ...
随机推荐
- Java编程知识点梳理
1. elementAt() temp.elementAt(0) 返回temp这个vector里面存放的第一个元素--->也是一个vector类型. 2. 字符串空格分割 String [] ...
- Linq------连表查询
1 List<Student> list = new List<Student>() { ,sex="男"}, ,sex="男"}, , ...
- Nginx系列p1:安装
学习新的知识都要从搭建环境开始,今天我们就来学习搭建一个 Nginx 环境. 环境:Ubuntu16.04 STL Nginx 1.16.1 Stable version 前言:当然可以通过 apt- ...
- pc页面在移动端浏览时部分字体放大,与pc字体大小不一致(Font Boosting)
最近做一个页面时,需要pc的页面在移动端浏览时保持pc的样式缩小.但是发现部分文字被放大了.看上去特别诡异.如下图 绿框是PC端查看时的正常样式,红框是移动端看的字体放大的诡异样式 是什么原因呢? 后 ...
- 小程序Java多次请求Session不变
微信小程序每次请求的sessionid是变化的,导致对应后台的session不一致,无法获取之前保存在session中的openid和sessionKey. 为了解决这个问题,需要强制同意每次小程序前 ...
- 吴裕雄--天生自然ShellX学习笔记:Shell printf 命令
printf 命令模仿 C 程序库(library)里的 printf() 程序. printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好. printf ...
- py02_02:pyc的解释
1. Python是一门解释型语言吗? 我初学Python时,听到的关于Python的第一句话就是,Python是一门解释性语言,我就这样一直相信下去,直到发现了*.pyc文件的存在.如果是解释型语言 ...
- 使用Euclid算法求最大公约数
参考文章 1.<linux c编程一站式学习>的习题5.3.1 2.百度百科Euclid算法:https://baike.baidu.com/item/Euclid%E7%AE%97%E6 ...
- 关于tomcat启动错误:At least one JAR was scanned for TLDs yet contained no TLDs
一.问题原因: 1.出现这个问题的原因就是Tomcat启动时会扫描大量jar包,如果含有不符合TLD规范的就会出现这个问题 2.以后基本上不会使用JSP作为视图层,所以我们可能根本不需要TLD这个东西 ...
- 17.3.13--python编码问题
1----字符编码: 字符编码(英语:Character encoding).字集码是把字符集中的字符编码为指定集合中某一对象(例如:比特模式.自然数串行.8位组或者电脉冲),以便文本在计算机中存储和 ...