此文章篇幅较长,平日上班较少时间写作,请见谅。持续更新中。。。
oAuth2.0系列文章目录
前言
本文用最基本的Spring Security,Spring Boot,不采用Spring Boot oAuth相关插件,重写oAuth2.0 SSO授权服务器。应用场景:客户端应用和授权服务器端属于同一个集团,处于不同微服务内;授权服务器和资源服务器处于相同服务器内。
一、具体实现及流程
用户在授权服务器注册用户(Happy Flow)
用户登录SSO(Happy Flow)
二、具体实现步骤
1. 项目设置
1.1 引入Maven依赖库
<!-- Core Functions Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Web Modules Dependencies -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>5.1.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery-ui</artifactId>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
<version>5.15.4</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
<!-- DB and Data Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- END Spring Boot default jdbc connector -->
<!-- Other Functions Dependencies -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
1.2 设置application.yml 和 application.properties
- application.yml
server:
port: 12911
servlet:
context-path: /oauth2
spring:
profiles:
active:
- default
application:
name: hp-auth-oauth2-sso-api-12911
- application.properties
#spring.datasource.url=jdbc:mysql://192.168.31.54:3306/PRIVILEGE?useSSL=false&useUnicode=true&characterEncoding=utf8
spring.datasource.url=jdbc:mysql://[your domain name]:3306/[your db name]?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=[your db username]
spring.datasource.password=[your db password]
# Hikari will use the above plus the following to setup connection pooling
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
#"none", This is the default for MySQL, no change to the database structure.
#update Hibernate changes the database according to the given Entity structures.
#create Creates the database every time, but don't drop it when close.
#create-drop Creates the database then drops it when the SessionFactory closes.
#It is good security practice that after your database is in production state,
# you make this none and revoke all privileges from the MySQL user connected to the Spring application,
# then give him only SELECT, UPDATE, INSERT, DELETE.
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.show-sql=true
#Unfortunately, Open Session in View (OSIV) is enabled by default in Spring Boot.
#So, make sure that setting: "spring.jpa.open-in-view=false"
#so that you can handle the LazyInitializationException the right way.
spring.jpa.open-in-view=false
# Hibernate properties
spring.jpa.properties.hibernate.id.new_generator_mappings=false
#thymelead properties
spring.mvc.static-path-pattern=/static/**
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
2. 后端开发
2.1 创建Login Controller
package com.hivesplace.templates.controllers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import com.hivesplace.templates.VOs.CsrfTokenJSessionIdVO;
import com.hivesplace.templates.beans.CsrfTokenJSessionId;
@Controller
public class LoginController {
@Autowired
CsrfTokenJSessionIdVO csrfTokenJSessionIdVO;
@GetMapping(path="/login")
public String login(HttpServletRequest request) {
csrfTokenJSessionIdVO.setCsrfToken(new CsrfTokenJSessionId().getCsrfToken());
csrfTokenJSessionIdVO.setJSessionId(request.getSession().getId());
System.out.println(">>>>>>>>>>> csrfTokenJSessionVO: "+csrfTokenJSessionIdVO.toString());
return "login";
}
}
2.2 创建POJO - CsrfTokenJSessionId
package com.hivesplace.templates.beans;
import java.util.Random;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
public class CsrfTokenJSessionId {
public CsrfTokenJSessionId() {
this.setCsrfToken();
this.jSessionId = "[please provide jSessionId by calling setter function - setJSessionId(String)]";
}
public CsrfTokenJSessionId(String csrfToken, String jSessionId) {
this.csrfToken = csrfToken;
this.jSessionId = jSessionId;
}
private String csrfToken;
private String jSessionId;
public void setCsrfToken() {
final boolean USELETTERS = true;
final boolean USENUMBERS = true;
final int BEGININDEX = new Random().nextInt(32);
final int ENDINDEX = BEGININDEX+32;
this.csrfToken = RandomStringUtils.random(64, USELETTERS, USENUMBERS).substring(BEGININDEX, ENDINDEX);
}
}
2.3 创建index.html (基于Webjars Bootstrap and jQuery)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/oauth2/static/img/favicon.ico">
<title>HIVESPLACE | Login</title>
<!-- Bootstrap core CSS -->
<link href="/oauth2/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/oauth2/static/css/signin.css" rel="stylesheet">
<link href="/oauth2/static/css/corporate.css" rel="stylesheet">
</head>
<body class="text-center">
<header class="display-none" id="loginWrap_header">
<div class="corporate__header">
<div class="header__wrapper">
<div class="header__item">
<nav class="top-bar">
<span>
<div class="top-bar__holder">
<a href="#" target="_blank"><span class="top-bar__txt">HIVESPLACE</span></a>
</div>
<div class="top-bar__holder">
<a href="#" target="_blank"><span class="top-bar__txt">About HIVESPLACE</span></a>
</div>
<div class="top-bar__holder">
</div>
</span>
</nav>
</div>
</div>
</div>
</header>
<div class="form-container row display-none" id="loginWrap_form">
<div class="form-head-container my-auto">
<div class="form-head">
<img id="login-logo" class="mb-4" src="/oauth2/static/img/logoBee320x346.png" alt="">
<h1 class="h3 mb-3 font-weight-normal">SSO Login</h1>
</div>
<div class="all-forms row">
<form class="form-signin needs-validation" method="post" action="oauth-login" novalidate>
<div class="uname-pword">
<p>请输入用户名及密码</p>
<input type="text" id="inputUsername" name="username" class="form-control login-uname" placeholder="Username" autocomplete="username" required>
<div class="form-group pass_show">
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" autocomplete="current-password" required>
<span class="ptxt" id="showPassword">Show</span>
</div>
<button class="btn btn-lg btn-primary btn-block mt-5 btn-submit" id="autoSignIn" type="submit">登入</button>
</div>
</form>
<div style="clear:both;"></div>
</div>
</div>
</div>
<!-- Footer -->
<footer class="sticky-footer bg-white display-none" id="loginWrap_footer">
<div class="container my-auto">
<div class="copyright text-center my-auto">
<span>版权 © 2021 HIVESPLACE.com</span>
</div>
</div>
</footer>
<!-- End of Footer -->
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/oauth2/webjars/jquery/jquery.min.js"></script>
<script src="/oauth2/webjars/bootstrap/js/bootstrap.min.js"></script>
<script src="/oauth2/static/js/signin.js"></script>
</body>
</html>
signin.js
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
'use strict';
window.addEventListener('load', function() {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
}, false);
//if(sessionStorage.autoLogin!=1){
//console.log("session storage - username: "+sessionStorage.autoUsername);
$("#loginWrap_header").removeClass("display-none");
$("#loginWrap_form").removeClass("display-none");
$("#loginWrap_footer").removeClass("display-none");
/*}else{
console.log("session storage - username: "+sessionStorage.autoUsername);
$("#inputUsername").val(sessionStorage.autoUsername);
$("#inputPassword").val(sessionStorage.autoPassword);
$("#autoSignIn").click();
}*/
})();
// jQuery for show password function
$('#showPassword').click(function(e){
/*
jQuery 使用函数来设置内容
语法: $(selector).attr(attribute,function(index,oldvalue))
*/
$(this).text($(this).text() == "Show" ? "Hide" : "Show");
/*
jQuery 使用函数来设置属性/值
语法: $(selector).attr(attribute,function(index,oldvalue))
*/
$(this).prev().attr('type', function(index, attr){return attr == 'password' ? 'text' : 'password'; });
});
// Stop redirect after validation pass & switch to SMS validation
var wrapWidth = $(".form-container").outerWidth();
总结
以上就是今天所讲的全部内容。
源代码
关注我并发表不少于10字评论可以获取本文源代码。
码农经典语录
Linus Torvalds
Talk is cheap, show me the code.
蜂窝码农
- DRY Principle (Don’t Repeat Yourself) 是做技术的最大笑话, DRY Principle应该改成 DORY Principle (Do Repeat Yourself)才对
- 没有中国参与的标准,不能称为世界标准。
俗语
好读书、好记性不如烂笔头
参考链接
- Mermaid https://mermaid-js.github.io/mermaid/#/
- Apache Tomcat, which version to use: https://tomcat.apache.org/whichversion.html