为何学习spring security? 理由如下:

1)虽然可以不用,但难免部分客户又要求

2)某种程度上,security还是不错的,譬如csrf,oauth等等,省了一些功夫。

3)虽然spring security 比较庞杂,甚至有些臃肿,但权衡之下,还是可以一学!。

根据很多网络例子和书籍来试验,都没有成功,原因可能是:

1)某些地方配置错了

2)使用的版本和他人不同

费了不少功夫。

一气之下,直接使用2.3.4的版本,成功了!

毫无疑问,2.3.4比以往的更加人性化。

以下内容比较长,可能需要耗费10分钟以上时间阅读。

自定义的关键在于几点。

一、pom配置+spring配置

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>learning</groupId>
<artifactId>secutiry</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>secutiry</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 支持jsp -->
<!-- 添加 servlet 依赖. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 添加 JSTL(JSP Standard Tag Library,JSP标准标签库) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- Jasper是tomcat中使用的JSP引擎,运用tomcat-embed-jasper可以将项目与tomcat分开 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
#热部署
spring.devtools.livereload.enabled=true
spring.devtools.restart.enabled=true
#服务器
server.port=8888
#bean 相关
#bean延迟启动
spring.main.lazy-initialization=false
#安全
#spring.security.user.name=root
#spring.security.user.password=root
server.servlet.context-path = /
#优雅关闭--等待还有的连接完成,之后不再允许有新的请求,类似于一些数据库的操作
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=20s
#设置静态资源等
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=/css/,/images/,/WEB-INF/plugin/ #启动jsp功能
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
# 数据库连接(mysql)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url =jdbc:mysql://127.0.0.1:7799/spring?rewriteBatchedStatements=true&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=CST&allowPublicKeyRetrieval=true
spring.datasource.username =lzf
spring.datasource.password =123 #连接池配置-HikariCp-----------------------------------------------------
#鉴于springboot目前的版本,spring.datasource.type也可以不写
spring.datasource.name=hcmdmserverDs
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#是否自动提交,默认true
spring.datasource.hikari.autoCommit=false
#连接超时,过了这个时间还连接不到,hikari会返回错误,设置3分钟
spring.datasource.hikari.connectionTimeout=180000
#多了多少毫秒不用,会被设置为空闲,默认是10分钟
spring.datasource.hikari.idleTimeout=600000
#最大生命周期,默认1800000(30分钟)
spring.datasource.hikari.maxLifetime=1800000
#最少空闲连接
spring.datasource.hikari.minimumIdle=3
#最大连接池大小=空闲+在用
spring.datasource.hikari.maximumPoolSize=6
spring.datasource.hikari.connection-test-query=select 1
#如果不指定 spring.datasource.type,则以下可以是通用的连接池配置信息 #spring-jdbc #http-请求连接池

二、org.springframework.security.core.userdetails.UserDetailsService实现类

这个部分的关键是两点:

1)实现UserDetailsService的时候,返回一个UserDetails即可

2)用户密码不需要像一些地方说的那样要有{bcrypt}之类的前缀

/**
*
* @author lzfto
* @apiNote
*/
@Service
public class UdsDetail implements UserDetailsService { @Autowired
MyUserService usersService; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserRolePojo ur = usersService.getUserRoleDetail(username);
return change(ur);
} private UserDetails change(UserRolePojo ur) {
List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>();
List<RolePojo> roleList = ur.getRoleList();
for (RolePojo role : roleList) {
GrantedAuthority gt = new SimpleGrantedAuthority(role.getName());
authorityList.add(gt);
}
UserDetails user = new User(ur.getUserPojo().getName(), ur.getUserPojo().getPassword(), authorityList);
return user;
}
}

至于如何和数据库关联,还是比较简单的,不需赘述!

此处附上插入用户信息的脚本:

-- 学生表
CREATE TABLE `users` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`password` varchar(70) NOT NULL,
`create_time` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
create table role(
id int not null auto_increment,
name varchar(40) not null,
primary key(id)
); create table user_role_detail(
id int not null auto_increment,
user_id int not null,
role_id int not null,
primary key(id)
);
create unique index uid_user_role_detail on user_role_detail(user_id,role_id); --
-- 密码是123
-- 不需要添加什么 bcrypt之类的前缀,在2.3.4版本中。
INSERT INTO `spring`.`users` (`name`, `password`, `create_time`) VALUES ('lzf', '$2a$10$Mg8XzxbqsOMQAxrPD8d9hOELzDyGc7lShVdSb7vOLWwEplWlga7cO', '2020-08-11 12:00:00');
INSERT INTO `spring`.`users` (`name`, `password`, `create_time`) VALUES ('wth', '$2a$10$Mg8XzxbqsOMQAxrPD8d9hOELzDyGc7lShVdSb7vOLWwEplWlga7cO', '2020-08-11 12:00:00');
--
INSERT INTO `spring`.`role` (`name`) VALUES ('ADMIN');
INSERT INTO `spring`.`role` (`name`) VALUES ('MANAGER');
INSERT INTO `spring`.`role` (`name`) VALUES ('LEADER');
INSERT INTO `spring`.`role` (`name`) VALUES ('CEO');
--
insert into user_role_detail(user_id,role_id) select u.id,r.id from users u,role r where u.name='lzf';
insert into user_role_detail(user_id,role_id) select u.id,r.id from users u,role r where u.name='wth' and r.name!='ADMIN';

三、WebSecurityConfigurerAdapter等有关配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
UdsDetail uService; @Override
protected void configure(HttpSecurity http) throws Exception {
/**
* 1.需要把loginPage,loginProcessingUrl放开 permiAll,否则会进入无限循环重定向
* 2.antMatchers("/doLogin", "/touch","/error404").permitAll()的顺序不重要
* 3.无需要配置 scanBasePackages
*/
http
.formLogin()
.loginPage("/doLogin")
.loginProcessingUrl("/touch")
.and()
.authorizeRequests()
.antMatchers("/doLogin", "/touch","/error404","/plugin/*").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(uService).passwordEncoder(encoder);
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/plugin/**", "/css/**", "/images/**");
} }

第一次看别人的"configure(HttpSecurity http)"方法的时候,

总有疑问:

  • "loginProcessingUrl"是不是一定是/login?
  • 是不是可以是其它的?
  • 就算是/login,那么是否需要在控制器中定义一个对应的方法?

明确的答案见最后一个小节:“创建login.jsp”,此处不再赘述。

为了让系统找到/doLogin,必须定义一个控制器方法

    @RequestMapping("/doLogin")
public ModelAndView skipLogin() {
ModelAndView mv = new ModelAndView("login");
return mv;
}

有的人不喜欢使用控制器,直接使用某个页面替代,譬如的login.jsp。这就是存粹的个人习惯了!

应用启动类:

@SpringBootApplication
public class SecutiryApplication {
public static void main(String[] args) {
SpringApplication.run(SecutiryApplication.class, args);
} }

SpringBootApplication无需具有 scanBasePackages的语法,因为那样会导致springboot使用默认的WebSecurityConfigurerAdapter 覆盖用户自己自定义的类,譬如上文的WebSecurityConfig

四、模板引擎,个人推荐使用jsp

为什么使用springboot+jsp,是因为springboot搭建mvc的确方便,其次jsp是公司大部分人都会,都熟悉的语言,而thymeleaf之类的模板引擎

额外增加了学习成本,但我们的项目并没有那么cloud。

所以综合起来,使用springboot+jsp是不错的

五、目录结构

在src/main/下创建:

/src/main/webapp/WEB-INF

/src/main/webapp/WEB-INF/jsp   (放jsp文件)

/src/main/webapp/plugin     (放jsp第三方插件,譬如jquery,vue,bootstrap等)

/src/main/webapp/css        (放公共样式)

/src/main/webapp/resource  (放图片等资源)

六、创建登录页面login.jsp(放在/src/main/webapp/WEB-INF/jsp下面)

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>登录测试页面(jsp)</title>
</head> <body>
<h2>自定义登录表单(jsp)</h2>
<div>
<br />
<!-- action 叫什么无所谓,只需要和 WebSecurityConfigurerAdapter 中的 loginProcessingUrl 值一致即可 -->
<form method="post" action="/touch">
用户名:
<input type="text" id="username" name="username" placeholder="name"><br />
密码:
<input type="password" id="password" name="password" placeholder="password"><br />
<input type="button" value="提交" onclick="fnLogin()">
<!-- <input type="submit" value="提交" > -->
</form> </div>
</body>
<script type="text/javascript" src="/plugin/jquery-3.4.1.min.js"></script>
<script>
function fnLogin(){
let uName=$("#username").val();
let pwd=$("#password").val();
$.ajax({
method: "post",
url: "/login",
cache: false,
async: false,
data: {
"username":uName,
"password":pwd
},
success: function (data) {
//location.href ="/test/main";
alert("good");
},
error: function (data) {
console.log(data);
}
});
}
</script> </html>

如果想使用form提交,那么,提交按钮如下设置:

<!-- <input type="button"   value="提交"  onclick="fnLogin()"> -->
<input type="submit" value="提交" >

反之,如果想使用ajax请求,那么对上面的语句反向注释即可:

 <input type="button"   value="提交"  onclick="fnLogin()">
<!-- <input type="submit" value="提交" > -->

使用ajax请求的关键在于设定参数和url。
注:如果需要这么使用,必须保证先继承UsernamePasswordAuthenticationFilter或者那个抽象父类AbstractAuthenticationProcessingFilter ,改写有关内容。
如果不是很有必要,就还是老老实实使用默认的form提交。

而url在没有修改的情况下是默认指定为/login,这是 在 UsernamePasswordAuthenticationFilter已经定义了,如下文:
/*
* @author Ben Alex
* @author Colin Sampaleanu
* @author Luke Taylor
* @since 3.0
*/
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
// ~ Static fields/initializers
// ===================================================================================== public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true; // ~ Constructors
// =================================================================================================== public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
那么是否可以不用/login? 答案是可以的! 
怎么做? 继承AbstractAuthenticationProcessingFilter ,然后在自定义的WebSecurityConfigurerAdapter 中配置一个新的过滤器。
假定这个继承的过滤器叫MyAuthFilter,那么MyAuthFilter在完成验证之后,就直接绕过原有的UsernamePasswordAuthenticationFilter等后续验证。
当然这样的做法并不是太好!
但这是为了告诉我们验证url是可以修改的,而不必都是/login。

springboot+security自定义登录-1-基础-自定义用户和登录界面的更多相关文章

  1. springboot+security整合(3)自定义鉴权

    说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...

  2. springboot+security整合(2)自定义校验

    说明 springboot 版本 2.0.3源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+se ...

  3. mysql用户修改登录密码及授予用户远程登录权限

    一.修改用户登录密码: mysql> show databases;ERROR 1820 (HY000): You must SET PASSWORD before executing this ...

  4. ASP.net 实现禁止用户重复登录

    本文先为大家介绍如何利用缓存Cache方便地实现此功能. Cache与Session这二个状态对像的其中有一个不同之处,Cache是一个全局对象,作用的范围是整个应用程序,所有用户:而Session是 ...

  5. Easyui + asp.net MVC 系列教程 第19-23 节 完成注销 登录限制过滤 添加用户

    前面视频 文章地址 Easyui + asp.net MVC 系列教程 第09-17 节 完成登录 高清录制  Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门  ...

  6. Linux限制某些用户或IP登录SSH、允许特定IP登录SSH

    说明:一般要实现这种功能时,先安装VPN,然后客户端登录VPN,然后通过内网IP登录SSH. 搭建OpenVPN: 参考:http://www.cnblogs.com/EasonJim/p/83338 ...

  7. linux普通用户无法登录mysql

    一.前言 本帖方法只适用于普通用户无法登录,但root用户可以登录的情况. 今天将war包放入linux后,运行报错,经过检查发现是数据库连接不上.奇怪的是,用户名和密码都是正确的,所以有了以下发现. ...

  8. Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战

    一. 前言 [APP 移动端]Spring Security OAuth2 手机短信验证码模式 [微信小程序]Spring Security OAuth2 微信授权模式 [管理系统]Spring Se ...

  9. Spring Security入门(2-3)Spring Security 的运行原理 4 - 自定义登录方法和页面

    参考链接,多谢作者: http://blog.csdn.net/lee353086/article/details/52586916 http元素下的form-login元素是用来定义表单登录信息的. ...

  10. Spring Security 实战干货:实现自定义退出登录

    文章目录 1. 前言 2. 我们使用 Spring Security 登录后都做了什么 2. 退出登录需要我们做什么 3. Spring Security 中的退出登录 3.1 LogoutFilte ...

随机推荐

  1. [Contract] ETH 与 Gas 之间的价格转换关系, Ethereum Gas Price Chart

    以太坊网络每天的平均气价(Gas)是变化,有一张价格表:https://etherscan.io/chart/gasprice 然后你可以知道 1 Gas = xx Gwei,再换算一下 1 ETH ...

  2. 第二讲 Cadence建立工程和元件库

    第二讲 Cadence建立工程和元件库 1.创建工程,设置图纸参数.Design Entry CIS / Orcad Capture CIS / Option /Design Template,可以设 ...

  3. Photoshop批量替换图层的方法

    平时做图片,应该有遇到这样的场景,比如P奖状.P邀请函,内容是一样的,但是图片上的名字是不一样的,要是要P100张的话,一个个手动复制改名字肯定会吐血(╯°□°)╯︵ ┻━┻ Photoshop里有个 ...

  4. Winform程序使用app.minifest清单禁止高DPI无法失效问题

    问题:Winform程序使用app.minifest清单禁止高DPI无法失效问题 摘要:因为笔记本基本都会有DPI放大,所以目前程序需要嵌入清单,并将其高DPI支持给禁止掉. 环境搭建:Winform ...

  5. golang向上取整、向下取整和四舍五入

    一.概述 官方的math 包中提供了取整的方法,向上取整math.Ceil() ,向下取整math.Floor() 二.用法 package main import ( "fmt" ...

  6. java的jdbc插入的时候,遇到null情况报错问题

    分析原因: 在执行SQL时MyBatis会自动通过对象中的属性给SQL中参数赋值,它会自动将Java类型转换成数据库的类型.而一旦传入的是null它就无法准确判断这个类型应该是什么,就有可能将类型转换 ...

  7. Windows有自带的远程桌面 为啥还要商业远程桌面

    网上有一类观点:最好的远程桌面就是windows自带的远程桌面. 那我们打破砂锅问到底,亲手实践下看看. 首先,我们来到了windows官网-远程桌面介绍页面. 如何使用远程桌面 设置你想要连接以使其 ...

  8. LLM实战:LLM微调加速神器-Unsloth + LLama3

    1. 背景 五一结束后,本qiang~又投入了LLM的技术海洋中,本期将给大家带来LLM微调神器:Unsloth. 正如Unsloth官方的对外宣贯:Easily finetune & tra ...

  9. 线程中使用for循环的add或remove方法的两种方案

    简介 (Introduction): 背景 在使用线程中添加list的元素时,使用add或remove就会产生异常. 分析 该list每当删除/添加一个元素时,集合的size方法的值都会减小1,这将直 ...

  10. SpringMVC 项目集成 PageOffice V6 最简单代码

    本文描述了PageOffice产品在SpringMVC项目中如何集成调用. 新建SpringMVC项目:pageoffice6-springmvc-simple 在您项目的pom.xml中通过下面的代 ...