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 ...
随机推荐
- 工程日记之HelloSlide(2) : UITextView中如何根据给定的长宽,计算最合适的字体大小
需求描述 一般的需求是将UITextview的大小自适应文本高度,会做出随文本内容增加,文字框不断增大的效果: 本文反其道而行之,在给定文字框大小的情况下:字数越多,字体越小: 需求来源: 考虑将文字 ...
- 修改默认SQL字符集
Setup /QUIET /ACTION=REBUILDDATABASE /INSTANCENAME=MSSQLSERVER /SQLSYSADMINACCOUNTS=lab\sccmadmin /S ...
- elastic启动脚本
#!bin/bash export JAVA_HOME=/usr/java/jdk1.8.0_144 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH ...
- Codeforces 997A Convert to Ones(思维)
https://codeforces.com/problemset/problem/997/A 题目大意: 给定一串0-1序列,定义两种操作: 操作一:选取一连续串倒置. 操作二:选取一连续串把进行0 ...
- HDU 1298 T9 字典树+DFS
必须要批评下自己了,首先就是这个题目的迟疑不定,去年做字典树的时候就碰到这个题目了,当时没什么好的想法,就暂时搁置了,其实想法应该有很多,只是居然没想到. 同样都是对单词进行建树,并插入可能值,但是拨 ...
- F5双机冗余配置
转自:https://blog.51cto.com/dynamic/769888本文作者:CTO_LiuJinFeng 1. 配置管理接口-IP 前面文章中没提到如何配置IP,现在开始来配置. 登录- ...
- 吴裕雄--天生自然MySQL学习笔记:MySQL 处理重复数据
有些 MySQL 数据表中可能存在重复的记录,有些情况允许重复数据的存在,但有时候我们也需要删除这些重复的数据. 防止表中出现重复数据 可以在 MySQL 数据表中设置指定的字段为 PRIMARY K ...
- 吴裕雄--天生自然 PHP开发学习:While 循环
<html> <body> <?php $i=1; while($i<=5) { echo "The number is " . $i . &q ...
- SQL 2005 带自增列 带外键约束 数据导入导出
1,生成建表脚本 选中要导的表,点右键-编写表脚本为-create到 ,生成建表脚本 2,建表(在新库),但不建外键关系 不要选中生成外键的那部分代码,只选择建表的代码 3,导数据,用SQL STU ...
- ZJNU 2351 - 快乐
由题意得,如果有个人从前往后能找到第一个不低于自己等级的任务,就会接取其后所有任务 那么就可以让输入数据处理成递增数列 例如1 3 5 4 6 2 7 7 3 可以处理成1 3 5 5 6 6 7 7 ...