整理了依赖版本问题,完成OAuth2客户端单点登录功能
This commit is contained in:
parent
f47bae8f9f
commit
9a2cb4bad3
@ -14,24 +14,24 @@ import org.springframework.stereotype.Component;
|
||||
@ConfigurationProperties(prefix = "security.oauth2")
|
||||
public class OAuth2ClientProperties {
|
||||
|
||||
private String oauth2Server;
|
||||
private String oauth2Logout;
|
||||
private String oauthServer;
|
||||
private String oauthLogout;
|
||||
private ClientProperties client;
|
||||
|
||||
public String getOauth2Server() {
|
||||
return oauth2Server == null ? "" : oauth2Server.trim();
|
||||
public String getOauthServer() {
|
||||
return oauthServer == null ? "" : oauthServer.trim();
|
||||
}
|
||||
|
||||
public void setOauth2Server(String oauth2Server) {
|
||||
this.oauth2Server = oauth2Server;
|
||||
public void setOauthServer(String oauthServer) {
|
||||
this.oauthServer = oauthServer;
|
||||
}
|
||||
|
||||
public String getOauth2Logout() {
|
||||
return oauth2Logout == null ? "" : oauth2Logout.trim();
|
||||
public String getOauthLogout() {
|
||||
return oauthLogout == null ? "" : oauthLogout.trim();
|
||||
}
|
||||
|
||||
public void setOauth2Logout(String oauth2Logout) {
|
||||
this.oauth2Logout = oauth2Logout;
|
||||
public void setOauthLogout(String oauthLogout) {
|
||||
this.oauthLogout = oauthLogout;
|
||||
}
|
||||
|
||||
public ClientProperties getClient() {
|
||||
|
@ -8,8 +8,8 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
@ -52,7 +52,7 @@ public class TransactionConfig {
|
||||
@Autowired
|
||||
private TransactionProperties transactionProperties;
|
||||
@Autowired
|
||||
private TransactionManager transactionManager;
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
/**
|
||||
* 事务拦截器
|
||||
|
@ -47,7 +47,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
baseProperties.getLoginProcess(),
|
||||
baseProperties.getLoginFailure(),
|
||||
"/oauth/**",
|
||||
"/oauth_client/**",
|
||||
"/oauth2_client/**",
|
||||
"/app/**",
|
||||
"/approute/**",
|
||||
"/wechat/**",
|
||||
|
@ -1,113 +0,0 @@
|
||||
package ink.wgink.login.base.security;
|
||||
|
||||
import ink.wgink.common.handler.AccessDenyHandler;
|
||||
import ink.wgink.login.base.handler.LoginFailureHandler;
|
||||
import ink.wgink.login.base.handler.LogoutHandler;
|
||||
import ink.wgink.login.base.security.user.UserSecurityConfig;
|
||||
import ink.wgink.login.base.service.user.UserDetailServiceImpl;
|
||||
import ink.wgink.login.base.service.user.UserLoginService;
|
||||
import ink.wgink.properties.BaseProperties;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* @ClassName: WebSecurityConfig
|
||||
* @Description: security配置
|
||||
* @Author: WangGeng
|
||||
* @Date: 2019/2/15 10:05 AM
|
||||
* @Version: 1.0
|
||||
**/
|
||||
//@EnableWebSecurity
|
||||
public class WebSecurityConfig1 {
|
||||
|
||||
@Autowired
|
||||
private BaseProperties baseProperties;
|
||||
@Autowired
|
||||
private UserDetailServiceImpl userDetailService;
|
||||
@Autowired
|
||||
private UserLoginService userLoginService;
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
/**
|
||||
* 默认放行配置
|
||||
*/
|
||||
String[] defaultAntMatchers = {
|
||||
baseProperties.getLoginUrl(),
|
||||
baseProperties.getLoginProcess(),
|
||||
baseProperties.getLoginFailure(),
|
||||
"/oauth/**",
|
||||
"/oauth_client/**",
|
||||
"/app/**",
|
||||
"/approute/**",
|
||||
"/wechat/**",
|
||||
"/wechat-miniapp/**",
|
||||
"/route/file/**",
|
||||
"/api/sms/getverificationcode/*",
|
||||
"/api/user/getsignintype/**"
|
||||
};
|
||||
String assetsMatchers = baseProperties.getAssetsMatchers();
|
||||
String[] fullAntMatchers;
|
||||
if (!StringUtils.isBlank(assetsMatchers)) {
|
||||
String[] assetsMatchersArray = baseProperties.getAssetsMatchers().split(",");
|
||||
fullAntMatchers = ArrayUtils.addAll(defaultAntMatchers, assetsMatchersArray);
|
||||
} else {
|
||||
fullAntMatchers = defaultAntMatchers;
|
||||
}
|
||||
|
||||
LoginFailureHandler loginFailureHandler = new LoginFailureHandler(baseProperties.getLoginFailure());
|
||||
http
|
||||
.formLogin()
|
||||
.loginPage(baseProperties.getLoginUrl())
|
||||
.loginProcessingUrl(baseProperties.getLoginProcess())
|
||||
.failureForwardUrl(baseProperties.getLoginUrl())
|
||||
.failureHandler(loginFailureHandler)
|
||||
.and()
|
||||
.logout()
|
||||
.addLogoutHandler(new LogoutHandler())
|
||||
.and()
|
||||
.headers()
|
||||
.frameOptions()
|
||||
.disable()
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(fullAntMatchers)
|
||||
.permitAll()
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.anyRequest().access("@rbacService.hasPermission(request, authentication)")
|
||||
.and()
|
||||
.exceptionHandling().accessDeniedHandler(new AccessDenyHandler())
|
||||
.and()
|
||||
.cors()
|
||||
.and()
|
||||
.csrf()
|
||||
.disable();
|
||||
addUserAuthenticationFilter(http, loginFailureHandler);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户认证过滤器链,替换原有UsernamePasswordAuthenticationFilter
|
||||
*
|
||||
* @param http
|
||||
* @param loginFailureHandler
|
||||
*/
|
||||
private void addUserAuthenticationFilter(HttpSecurity http, LoginFailureHandler loginFailureHandler) throws Exception {
|
||||
UserSecurityConfig userSecurityConfig = new UserSecurityConfig();
|
||||
userSecurityConfig.setUserDetailService(userDetailService);
|
||||
userSecurityConfig.setPasswordEncoder(passwordEncoder);
|
||||
userSecurityConfig.setLoginProcessUrl(baseProperties.getLoginProcess());
|
||||
userSecurityConfig.setLoginFailureHandler(loginFailureHandler);
|
||||
userSecurityConfig.setUserLoginService(userLoginService);
|
||||
http.apply(userSecurityConfig);
|
||||
}
|
||||
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<base th:href="${#request.getContextPath() + '/'}">
|
||||
<title th:text="${systemTitle}"></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="icon" type="image/ico" href="assets/favicon.ico"/>
|
||||
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css?v=3" media="all">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css?v=3" media="all">
|
||||
<link rel="stylesheet" href="assets/css/supersized.css?v=3">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/style/login.css?v=3" media="all">
|
||||
<style th:if="${loginBoxPosition eq 'center'}">
|
||||
.layadmin-user-login-main {
|
||||
left: 50%;
|
||||
margin-left: -188px;
|
||||
}
|
||||
</style>
|
||||
<style th:if="${loginBoxPosition eq 'left'}">
|
||||
@media screen and (max-width: 1920px) {
|
||||
.layadmin-user-login-main {
|
||||
right: 68%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1366px) {
|
||||
.layadmin-user-login-main {
|
||||
right: 62%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layadmin-user-login layadmin-user-display-show" id="LAY-user-login">
|
||||
<div id="layadminUserLoginMain" class="layadmin-user-login-main" style="">
|
||||
<div class="layadmin-user-login-box layadmin-user-login-header">
|
||||
<img class="system-logo" th:src="'route/file/download/true/'+ ${systemLogo}" th:if="${systemLogo ne ''}">
|
||||
<div th:class="${(systemLogo ne '') ? 'system-logo-title': ''}">
|
||||
<h2 th:text="${systemTitle}" th:style="'font-size:'+ ${systemTitleSize} +'px'"></h2>
|
||||
<p th:text="${systemSubTitle}" th:style="'font-size:'+ ${systemSubTitleSize} +'px'"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="userLoginBox" class="layadmin-user-login-box layadmin-user-login-body layui-form" lay-filter="LAY-form-signin">
|
||||
<form id="LAY-form-signin" action="userlogin" method="post">
|
||||
<input type="hidden" name="loginType" id="LAY-user-login-logintype" value="1">
|
||||
<input type="hidden" name="uKey" id="LAY-user-login-ukey" value="">
|
||||
<div class="layui-form-item">
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-username" for="LAY-user-login-username"></label>
|
||||
<input type="text" name="username" id="LAY-user-login-username" lay-verify="required" placeholder="用户名" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-item" id="passwordBox">
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-password" for="LAY-user-login-password"></label>
|
||||
<input type="password" name="password" id="LAY-user-login-password" lay-verify="required" placeholder="密码" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-item" id="verificationCodeBox" th:if="${verificationCode eq 'true'}">
|
||||
<!-- 验证码 -->
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs7">
|
||||
<label class="layadmin-user-login-icon layui-icon layui-icon-vercode" for="LAY-user-login-vercode"></label>
|
||||
<input type="text" name="verificationCode" id="LAY-user-login-vercode" lay-verify="required" placeholder="图形验证码" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-col-xs5">
|
||||
<div style="margin-left: 10px;">
|
||||
<img src="oauth/verification-code/png" class="layadmin-user-login-codeimg" id="LAY-user-get-vercode">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item u-key-box" id="uKeyBox" th:if="${uKeyLogin eq 'true'}">
|
||||
<!-- ukey登录 -->
|
||||
<span id="checkUKey">【检测UKey中...】</span>
|
||||
<span class="get-u-key" id="getUKey">【UKey已插入】</span>
|
||||
</div>
|
||||
<div class="layui-form-item remember-password" id="rememberPassword">
|
||||
<input type="checkbox" name="remember" lay-filter="LAY-user-login-remember" lay-skin="primary" title="记住密码">
|
||||
<!--<a href="forget.html" class="layadmin-user-jump-change layadmin-link" style="margin-top: 7px;">忘记密码?</a>-->
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<button class="layui-btn layui-btn-fluid" id="LAY-user-login-submit" lay-submit lay-filter="LAY-user-login-submit">登 入</button>
|
||||
</div>
|
||||
<div id="otherLoginBox" class="other-login-box" th:if="${scanCodeLogin ne 'false'}">
|
||||
<!-- 钉钉扫码 -->
|
||||
<div th:if="${scanCodeLogin eq 'dingDingScanCode'}">
|
||||
<img src="assets/images/login/dingding.png" style="width: 16px;">
|
||||
<a href="javascript:void(0);" id="dingDingScanCodeBtn">钉钉扫码</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="dingDingScanCodeBox" class="ding-ding-scan-code-box" th:if="${scanCodeLogin eq 'dingDingScanCode'}">
|
||||
<!-- 钉钉扫码登录 -->
|
||||
<div class="code-box">
|
||||
<div id="dingDingScanCode"></div>
|
||||
<a id="cancelDingDingScanCodeBtn" class="cancel-ding-ding-scan-code-btn" href="javascript:void(0);">取消扫码</a>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="errorMessage" th:value="${errorMessage}"/>
|
||||
</div>
|
||||
<div class="layui-trans layadmin-user-login-footer footer-text">
|
||||
<p th:if="${copyRightYear ne ''}"><span th:text="${'© '+ copyRightYear +' '}"></span><a href="javascript:void(0);" th:if="${copyleft ne ''}" th:text="${copyleft}"></a></p>
|
||||
<p th:if="${officialUrl ne ''}">
|
||||
<span><a th:href="@{${officialUrl}}" target="_blank">前往官网</a></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: none">
|
||||
<object type="application/x-dongle" id="uKey"></object>
|
||||
</div>
|
||||
<input type="hidden" id="serverUrl" th:value="${serverUrl +'/userlogin/dingding'}">
|
||||
<input type="hidden" id="dingDingScanCodeAppId" th:value="${dingDingScanCodeAppId}" th:if="${scanCodeLogin eq 'dingDingScanCode'}">
|
||||
<input type="hidden" id="loginBackgroundImages" th:value="${loginBackgroundImages}">
|
||||
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js" th:if="${scanCodeLogin eq 'dingDingScanCode'}"></script>
|
||||
<script src="assets/layuiadmin/layui/layui.js"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: 'assets/layuiadmin/'
|
||||
}).extend({
|
||||
index: 'lib/index'
|
||||
}).use(['index', 'user', 'cookie', 'md5', 'base64', 'supersized', 'common', 'ftukey', 'restajax'], function () {
|
||||
var $ = layui.$;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var cookie = layui.cookie;
|
||||
var md5 = layui.md5;
|
||||
var base64 = layui.base64;
|
||||
var common = layui.common;
|
||||
var ftukey = layui.ftukey;
|
||||
var restAjax = layui.restajax;
|
||||
var isCheckLoginType = false;
|
||||
|
||||
var pageData = {
|
||||
remember: false
|
||||
};
|
||||
// 显示错误信息
|
||||
var errorMessage = $('#errorMessage').val();
|
||||
if (errorMessage != '') {
|
||||
layer.msg(errorMessage);
|
||||
}
|
||||
// 初始化背景
|
||||
function initBackground() {
|
||||
var loginBackgroundImages = $('#loginBackgroundImages').val();
|
||||
var photos = [];
|
||||
if(loginBackgroundImages != '') {
|
||||
var loginBackgroundImageArray = loginBackgroundImages.split(',');
|
||||
for(var i = 0, item = loginBackgroundImageArray[i]; item = loginBackgroundImageArray[i++];) {
|
||||
photos.push({
|
||||
image: 'route/file/download/true/'+ item
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for(var i = 1; i <= 9; i++) {
|
||||
photos.push({
|
||||
image: 'assets/images/backgrounds/'+ i +'.jpg'
|
||||
})
|
||||
}
|
||||
}
|
||||
$.supersized({
|
||||
slide_interval : 4000, // Length between transitions
|
||||
transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left
|
||||
transition_speed : 1000, // Speed of transition
|
||||
performance : 1, // 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed // (Only works for Firefox/IE, not Webkit)
|
||||
// Size & Position
|
||||
min_width : 0, // Min width allowed (in pixels)
|
||||
min_height : 0, // Min height allowed (in pixels)
|
||||
vertical_center : 1, // Vertically center background
|
||||
horizontal_center : 1, // Horizontally center background
|
||||
fit_always : 0, // Image will never exceed browser width or height (Ignores min. dimensions)
|
||||
fit_portrait : 1, // Portrait images will not exceed browser height
|
||||
fit_landscape : 0, // Landscape images will not exceed browser width
|
||||
// Components
|
||||
slide_links : 'blank', // Individual links for each slide (Options: false, 'num', 'name', 'blank')
|
||||
slides : photos
|
||||
});
|
||||
}
|
||||
initBackground();
|
||||
|
||||
// 记住密码
|
||||
form.on('checkbox(LAY-user-login-remember)', function(data){
|
||||
pageData.remember = data.elem.checked;
|
||||
});
|
||||
|
||||
//提交
|
||||
form.on('submit(LAY-user-login-submit)', function (obj) {
|
||||
if(pageData.remember) {
|
||||
cookie.setCookie('rememberMe', base64.encode(obj.field.username +'_$cm$_'+ obj.field.password), 30);
|
||||
} else {
|
||||
cookie.setCookie('rememberMe', '', 0);
|
||||
}
|
||||
$('#LAY-user-login-password').val(md5(md5(md5(obj.field.password))));
|
||||
layer.msg('正在登录,请稍后...', {icon: 16, shade: 0.1, time: 0});
|
||||
});
|
||||
|
||||
$('#dingDingScanCodeBtn').on('click', function() {
|
||||
$('#userLoginBox').hide();
|
||||
var url = encodeURIComponent($('#serverUrl').val());
|
||||
var appId = $('#dingDingScanCodeAppId').val();
|
||||
var obj = DDLogin({
|
||||
id: 'dingDingScanCode',
|
||||
goto: encodeURIComponent('https://oapi.dingtalk.com/connect/qrconnect?appid='+ appId +'&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+ url),
|
||||
style: 'border:none; background-color:#FFFFFF;',
|
||||
width : '100%',
|
||||
height: '320'
|
||||
});
|
||||
$('#dingDingScanCodeBox').show();
|
||||
|
||||
var handleMessage = function (event) {
|
||||
var origin = event.origin;
|
||||
if( origin == "https://login.dingtalk.com" ) {
|
||||
var loginTmpCode = event.data;
|
||||
var snsAuthorizeHref = 'https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid='+ appId +'&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+ url +'&loginTmpCode='+ loginTmpCode;
|
||||
window.location.href = snsAuthorizeHref;
|
||||
}
|
||||
};
|
||||
// 添加监听事件
|
||||
if (typeof window.addEventListener != 'undefined') {
|
||||
window.addEventListener('message', handleMessage, false);
|
||||
} else if (typeof window.attachEvent != 'undefined') {
|
||||
window.attachEvent('onmessage', handleMessage);
|
||||
}
|
||||
});
|
||||
|
||||
$('#cancelDingDingScanCodeBtn').on('click', function() {
|
||||
$('#dingDingScanCodeBox').hide();
|
||||
$('#userLoginBox').show();
|
||||
});
|
||||
|
||||
// 记住我
|
||||
var rememberMe = cookie.getCookie('rememberMe');
|
||||
if(null != rememberMe && '' != rememberMe) {
|
||||
var rememberMes = base64.decode(rememberMe).split('_$cm$_');
|
||||
$('#LAY-user-login-username').val(rememberMes[0]);
|
||||
$('#LAY-user-login-password').val(rememberMes[1])
|
||||
pageData.remember = true;
|
||||
form.val('LAY-form-signin', {
|
||||
remember: true
|
||||
});
|
||||
}
|
||||
// 刷新表单
|
||||
form.render();
|
||||
|
||||
// 初始化表单
|
||||
function initLoginForm(uKeyData) {
|
||||
if(uKeyData.UserLoginName.length == 0) {
|
||||
return;
|
||||
}
|
||||
$('#LAY-user-login-username').val(uKeyData.UserLoginName);
|
||||
var loadLayerIndex;
|
||||
restAjax.get(restAjax.path('api/user/getsignintype/{userUsername}', [uKeyData.UserLoginName]), {}, null, function(code, data) {
|
||||
if(data.data != 1 && data.data != 2 && data.data != 3) {
|
||||
layer.msg('用户登录类型错误,请使用用户名和密码登录');
|
||||
return;
|
||||
}
|
||||
$('#LAY-user-login-logintype').val(data.data);
|
||||
if(data.data == 1) {
|
||||
return;
|
||||
}
|
||||
// 隐藏验证码
|
||||
if($('#verificationCodeBox')) {
|
||||
$('#verificationCodeBox').hide();
|
||||
}
|
||||
$('#LAY-user-login-vercode').removeAttr('lay-verify');
|
||||
if(data.data === 2) {
|
||||
return;
|
||||
}
|
||||
$('#LAY-user-login-password').removeAttr('lay-verify');
|
||||
$('#passwordBox').hide();
|
||||
$('#rememberPassword').hide();
|
||||
$('#uKeyBox').css('marginBottom', '20px');
|
||||
if(data.data === 3) {
|
||||
return;
|
||||
}
|
||||
}, function(code, data) {
|
||||
layer.msg(data.msg);
|
||||
}, function() {
|
||||
loadLayerIndex = layer.msg('查询登录类型,请稍后...', {icon: 16, time: 0, shade: 0.3});
|
||||
}, function() {
|
||||
layer.close(loadLayerIndex);
|
||||
});
|
||||
}
|
||||
// 检查UKey
|
||||
function checkUKey() {
|
||||
var ukeyData = ftukey.ReadUKeyData();
|
||||
if(ukeyData.success && ukeyData.data.length != 0) {
|
||||
if(!isCheckLoginType) {
|
||||
isCheckLoginType = true;
|
||||
$('#LAY-form-signin').attr('action', 'userlogin/ukey');
|
||||
$('#checkUKey').hide();
|
||||
$('#getUKey').show();
|
||||
setTimeout(function() {
|
||||
var data = ftukey.ParseJson(ukeyData.data);
|
||||
$('#LAY-user-login-ukey').val(ukeyData.hid);
|
||||
initLoginForm(data);
|
||||
}, 500);
|
||||
}
|
||||
} else {
|
||||
if(isCheckLoginType) {
|
||||
isCheckLoginType = false;
|
||||
$('#LAY-form-signin').attr('action', 'userlogin');
|
||||
$('#LAY-user-login-vercode').attr('lay-verify', 'required');
|
||||
$('#LAY-user-login-password').attr('lay-verify', 'required');
|
||||
if($('#verificationCodeBox')) {
|
||||
$('#verificationCodeBox').show();
|
||||
}
|
||||
$('#passwordBox').show();
|
||||
$('#rememberPassword').show();
|
||||
$('#uKeyBox').css('marginBottom', '0px');
|
||||
$('#getUKey').hide();
|
||||
$('#checkUKey').show();
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
checkUKey();
|
||||
}, 1000);
|
||||
}
|
||||
// 初始化UKey监听器
|
||||
function initUKeyListener() {
|
||||
if (common.getBrowserType() != 'IE') {
|
||||
return;
|
||||
}
|
||||
if (common.getIEBrowserVersion() != '11') {
|
||||
return;
|
||||
}
|
||||
$('#uKeyBox').show();
|
||||
setTimeout(function() {
|
||||
checkUKey();
|
||||
}, 1000);
|
||||
}
|
||||
if($('#uKeyBox')) {
|
||||
initUKeyListener();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -21,12 +21,27 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-jwt</artifactId>
|
||||
<version>1.0.9.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth.boot</groupId>
|
||||
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
|
@ -35,13 +35,13 @@ public class OAuth2ClientConfig extends WebSecurityConfigurerAdapter {
|
||||
.formLogin()
|
||||
.defaultSuccessUrl("/authorize", true)
|
||||
.and()
|
||||
.logout().logoutSuccessUrl(oAuth2ClientProperties.getOauth2Logout())
|
||||
.logout().logoutSuccessUrl(oAuth2ClientProperties.getOauthLogout())
|
||||
.and()
|
||||
.authorizeRequests().antMatchers("/app/**","/resource/**", "/route/file/**").permitAll()
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.anyRequest()
|
||||
.access("@rbacService.hasPermission(request, authentication)")
|
||||
.access("@clientRbacService.hasPermission(request, authentication)")
|
||||
.and()
|
||||
.headers().frameOptions().sameOrigin()
|
||||
.and()
|
||||
|
@ -0,0 +1,120 @@
|
||||
package ink.wgink.login.oauth2.client.controller.route;
|
||||
|
||||
import ink.wgink.common.component.SecurityComponent;
|
||||
import ink.wgink.interfaces.menu.IMenuBaseService;
|
||||
import ink.wgink.interfaces.role.IRoleMenuBaseService;
|
||||
import ink.wgink.pojo.bos.UserInfoBO;
|
||||
import ink.wgink.properties.ServerProperties;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
* 当你想要放弃的时候,想想当初你为何开始
|
||||
*
|
||||
* @ClassName: IndexRouteController
|
||||
* @Description: index
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/2/27 3:38 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Controller
|
||||
public class IndexRouteController {
|
||||
|
||||
@Autowired
|
||||
private SecurityComponent securityComponent;
|
||||
@Autowired
|
||||
private ServerProperties serverProperties;
|
||||
@Autowired(required = false)
|
||||
private IMenuBaseService menuBaseService;
|
||||
@Autowired(required = false)
|
||||
private IRoleMenuBaseService roleMenuBaseService;
|
||||
|
||||
@GetMapping("index")
|
||||
public ModelAndView goIndex() {
|
||||
if (!StringUtils.isBlank(serverProperties.getDefaultIndexPage())) {
|
||||
return new ModelAndView(new RedirectView(serverProperties.getDefaultIndexPage()));
|
||||
}
|
||||
return new ModelAndView("forward:/default-main");
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认主页
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("default-main")
|
||||
public ModelAndView defaultMain() {
|
||||
ModelAndView mv = new ModelAndView("default-main");
|
||||
UserInfoBO userInfoBO = securityComponent.getCurrentUser();
|
||||
mv.addObject("userUsername", userInfoBO.getUserUsername());
|
||||
// Map<String, Object> config = ConfigManager.getInstance().getConfig();
|
||||
// // 先加载系统短标题,没有加载主标题,没有加载配置文件系统标题
|
||||
// if (!Objects.isNull(config.get(IUserCenterConst.SYSTEM_SHORT_TITLE)) && !StringUtils.isBlank(config.get(IUserCenterConst.SYSTEM_SHORT_TITLE).toString())) {
|
||||
// mv.addObject(IUserCenterConst.SYSTEM_SHORT_TITLE, config.get(IUserCenterConst.SYSTEM_SHORT_TITLE).toString());
|
||||
// } else if(!Objects.isNull(config.get(IUserCenterConst.SYSTEM_TITLE)) && !StringUtils.isBlank(config.get(IUserCenterConst.SYSTEM_TITLE).toString())) {
|
||||
// mv.addObject(IUserCenterConst.SYSTEM_SHORT_TITLE, config.get(IUserCenterConst.SYSTEM_TITLE).toString());
|
||||
// } else {
|
||||
// mv.addObject(IUserCenterConst.SYSTEM_SHORT_TITLE, serverProperties.getSystemTitle());
|
||||
// }
|
||||
// // 系统短LOGO
|
||||
// if (!Objects.isNull(config.get(IUserCenterConst.SYSTEM_SHORT_LOGO)) && !StringUtils.isBlank(config.get(IUserCenterConst.SYSTEM_SHORT_LOGO).toString())) {
|
||||
// mv.addObject(IUserCenterConst.SYSTEM_SHORT_LOGO, config.get(IUserCenterConst.SYSTEM_SHORT_LOGO).toString());
|
||||
// } else {
|
||||
// mv.addObject(IUserCenterConst.SYSTEM_SHORT_LOGO, "");
|
||||
// }
|
||||
// mv.addObject("ws", serverProperties.getWs());
|
||||
// // 菜单模式
|
||||
// if (!Objects.isNull(config.get(IUserCenterConst.MENU_MODE)) && !StringUtils.isBlank(config.get(IUserCenterConst.MENU_MODE).toString())) {
|
||||
// mv.addObject(IUserCenterConst.MENU_MODE, config.get(IUserCenterConst.MENU_MODE).toString());
|
||||
// }
|
||||
// if (menuBaseService != null) {
|
||||
// List<MenuDTO> menus;
|
||||
// if (StringUtils.equalsIgnoreCase(ISystemConstant.ADMIN, userInfoBO.getUserUsername())) {
|
||||
// // 管理员
|
||||
// List<String> menuIds = roleMenuBaseService.listMenuId(ISystemConstant.ADMIN);
|
||||
// if (menuIds.isEmpty()) {
|
||||
// menus = menuBaseService.listAllByParentId(IMenuBaseService.MENU_UNIFIED_USER);
|
||||
// } else {
|
||||
// menus = menuBaseService.listAllByParentIdAndIds(IMenuBaseService.MENU_UNIFIED_USER, menuIds);
|
||||
// }
|
||||
// } else {
|
||||
// // 普通用户
|
||||
// List<RoleSimpleDTO> roleSimpleDTOs = securityComponent.getCurrentUser().getRoles();
|
||||
// if (roleSimpleDTOs.isEmpty()) {
|
||||
// menus = new ArrayList<>();
|
||||
// } else {
|
||||
// List<String> roleIds = new ArrayList<>();
|
||||
// for (RoleSimpleDTO roleSimpleDTO : roleSimpleDTOs) {
|
||||
// roleIds.add(roleSimpleDTO.getRoleId());
|
||||
// }
|
||||
// List<String> menuIds = roleMenuBaseService.listMenuId(roleIds);
|
||||
// menus = menuBaseService.listAllByParentIdAndIds(IMenuBaseService.MENU_UNIFIED_USER, menuIds);
|
||||
// }
|
||||
// }
|
||||
// mv.addObject("menus", menus);
|
||||
// }
|
||||
return mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认导航首页
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("default-home")
|
||||
public ModelAndView defaultHome() {
|
||||
ModelAndView mv;
|
||||
if (!StringUtils.isBlank(serverProperties.getDefaultHomePage())) {
|
||||
mv = new ModelAndView(new RedirectView(serverProperties.getDefaultHomePage()));
|
||||
} else {
|
||||
mv = new ModelAndView("default-home");
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ink.wgink.login.oauth2.client.converter;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import ink.wgink.pojo.bos.UserInfoBO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -36,7 +37,7 @@ public class OAuth2ClientUserAuthConverter implements UserAuthenticationConverte
|
||||
if (!Objects.isNull(principal)) {
|
||||
Collection<GrantedAuthority> authorities = new ArrayList<>();
|
||||
// 包含用户信息,则直接抽取其中的用户信息
|
||||
UserInfoBO userInfoBO = (UserInfoBO) map.get("user_info");
|
||||
UserInfoBO userInfoBO = JSONObject.parseObject(map.get("user_info").toString(), UserInfoBO.class);
|
||||
principal = userInfoBO;
|
||||
LOG.debug("获取用户权限");
|
||||
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
|
||||
|
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<base th:href="${#request.getContextPath() + '/'}">
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
|
||||
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="LAY-app" class="layui-fluid">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<h1>欢迎使用</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="assets/js/vue.min.js"></script>
|
||||
<script type="text/javascript" src="assets/js/vendor/echarts/echarts.min.js"></script>
|
||||
<script src="assets/layuiadmin/layui/layui.js"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: 'assets/layuiadmin/' //静态资源所在路径
|
||||
}).extend({
|
||||
index: 'lib/index' //主入口模块
|
||||
}).use(['index', 'animate-numbers'], function() {
|
||||
var $ = layui.$;
|
||||
var $win = $(window);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,248 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<base th:href="${#request.getContextPath() + '/'}">
|
||||
<title th:text="${systemTitle}"></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="icon" type="image/ico" href="assets/favicon.ico"/>
|
||||
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
|
||||
<link rel="stylesheet" href="assets/layuiadmin/style/default-main.css" media="all">
|
||||
<style th:if="${menuMode eq 'floatLeft'}">
|
||||
.layui-side-menu-hidden {display: none;}
|
||||
.layui-layout-admin .layui-layout-left, .layadmin-pagetabs, .layui-layout-admin .layui-body, .layui-layout-admin .layui-footer {left: 0px}
|
||||
@media screen and (max-width: 992px) {
|
||||
.layui-layout-admin .layui-side {
|
||||
transform: none;
|
||||
-webkit-transform: none;
|
||||
width: 220px;
|
||||
}
|
||||
.layui-layout-admin .layui-layout-left,
|
||||
.layadmin-pagetabs,
|
||||
.layui-layout-admin .layui-body,
|
||||
.layui-layout-admin .layui-footer {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="layui-layout-body">
|
||||
<div id="LAY_app">
|
||||
<div class="layui-layout layui-layout-admin">
|
||||
<div class="layui-header">
|
||||
<!-- 头部区域 -->
|
||||
<ul class="layui-nav layui-layout-left">
|
||||
<li class="layui-nav-item layadmin-flexible layui-side-menu-show" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="flexible" title="侧边伸缩">
|
||||
<i class="layui-icon layui-icon-shrink-right" id="LAY_app_flexible"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item layui-hide-xs" lay-unselect>
|
||||
<a href="index" title="首页">
|
||||
<i class="layui-icon layui-icon-website"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="refresh" title="刷新">
|
||||
<i class="layui-icon layui-icon-refresh-3"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="layui-nav layui-layout-right" lay-filter="layadmin-layout-right">
|
||||
<li class="layui-nav-item layui-hide-xs" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="theme">
|
||||
<i class="layui-icon layui-icon-theme"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item layui-hide-xs" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="note">
|
||||
<i class="layui-icon layui-icon-note"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item" lay-unselect>
|
||||
<a href="javascript:void(0);">
|
||||
<cite th:text="${userUsername}"></cite>
|
||||
</a>
|
||||
<dl class="layui-nav-child">
|
||||
<dd><a id="LAY-changePassword" lay-href="javascript:void(0);">修改密码</a></dd>
|
||||
<hr>
|
||||
<dd id="LAY-logout" style="text-align: center;"><a href="javascript:void(0);">退出</a></dd>
|
||||
</dl>
|
||||
</li>
|
||||
<li class="layui-nav-item layui-hide-xs" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="fullscreen">
|
||||
<i class="layui-icon layui-icon-screen-full"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-unselect>
|
||||
<a href="javascript:void(0);" layadmin-event="more"><i class="layui-icon layui-icon-more-vertical"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 侧边菜单 -->
|
||||
<div id="sideMenu" class="layui-side layui-side-menu layui-side-menu-hidden">
|
||||
<div class="layui-side-scroll">
|
||||
<div class="layui-logo">
|
||||
<img th:src="'route/file/download/true/'+ ${systemShortLogo}" style="width: 32px; height: 32px;" th:if="${systemShortLogo ne ''}">
|
||||
<span th:text="${systemShortTitle}" style="font-weight: bold;"></span>
|
||||
</div>
|
||||
|
||||
<ul class="layui-nav layui-nav-tree" lay-shrink="all" id="LAY-system-side-menu" lay-filter="layadmin-system-side-menu">
|
||||
<li data-name="component" class="layui-nav-item" th:each="menu1: ${menus}" th:title="${menu1.menuSummary}">
|
||||
<a href="javascript:void(0);" th:if="${!#lists.isEmpty(menu1.subMenus)}" th:attr="lay-tips=${menu1.menuName}" lay-direction="2">
|
||||
<span class="layui-icon">
|
||||
<i th:class="${menu1.menuIcon}"></i>
|
||||
</span>
|
||||
<cite th:text="${menu1.menuName}"></cite>
|
||||
</a>
|
||||
<a href="javascript:void(0);" th:if="${#lists.isEmpty(menu1.subMenus)}" th:attr="lay-tips=${menu1.menuName},lay-href=${menu1.menuUrl} ,lay-open-type=${menu1.openType}" lay-direction="2">
|
||||
<span class="layui-icon">
|
||||
<i th:class="${menu1.menuIcon}"></i>
|
||||
</span>
|
||||
<cite th:text="${menu1.menuName}"></cite>
|
||||
</a>
|
||||
|
||||
<dl class="layui-nav-child" th:if="${!#lists.isEmpty(menu1.subMenus)}">
|
||||
<dd data-name="grid" th:each="menu2: ${menu1.subMenus}" th:title="${menu2.menuSummary}">
|
||||
<a href="javascript:void(0);" th:if="${!#lists.isEmpty(menu2.subMenus)}">
|
||||
<span class="layui-icon">
|
||||
<i th:class="${menu2.menuIcon}"></i>
|
||||
</span>
|
||||
<cite th:text="${menu2.menuName}"></cite>
|
||||
</a>
|
||||
<a href="javascript:void(0);" th:if="${#lists.isEmpty(menu2.subMenus)}" th:attr="lay-href=${menu2.menuUrl}, lay-open-type=${menu2.openType}">
|
||||
<span class="layui-icon">
|
||||
<i th:class="${menu2.menuIcon}"></i>
|
||||
</span>
|
||||
<cite th:text="${menu2.menuName}"></cite>
|
||||
</a>
|
||||
|
||||
<dl class="layui-nav-child" th:if="${!#lists.isEmpty(menu2.subMenus)}">
|
||||
<dd data-name="list" th:each="menu3: ${menu2.subMenus}" th:title="${menu3.menuSummary}">
|
||||
<a href="javascript:void(0)" th:text="${menu3.menuName}" th:attr="lay-href=${menu3.menuUrl}, lay-open-type=${menu3.openType}"></a>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面标签 -->
|
||||
<div class="layadmin-pagetabs auto-hide-menu" id="LAY_app_tabs">
|
||||
<div class="layui-icon layadmin-tabs-control layui-icon-prev" layadmin-event="leftPage"></div>
|
||||
<div class="layui-icon layadmin-tabs-control layui-icon-next" layadmin-event="rightPage"></div>
|
||||
<div class="layui-icon layadmin-tabs-control layui-icon-down">
|
||||
<ul class="layui-nav layadmin-tabs-select" lay-filter="layadmin-pagetabs-nav">
|
||||
<li class="layui-nav-item" lay-unselect>
|
||||
<a href="javascript:void(0);"></a>
|
||||
<dl class="layui-nav-child layui-anim-fadein">
|
||||
<dd layadmin-event="closeThisTabs"><a href="javascript:void(0);">关闭当前标签页</a></dd>
|
||||
<dd layadmin-event="closeOtherTabs"><a href="javascript:void(0);">关闭其它标签页</a></dd>
|
||||
<dd layadmin-event="closeAllTabs"><a href="javascript:void(0);">关闭全部标签页</a></dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="layui-tab" lay-unauto lay-allowClose="true" lay-filter="layadmin-layout-tabs">
|
||||
<ul class="layui-tab-title" id="LAY_app_tabsheader">
|
||||
<li lay-id="default.html" lay-attr="default.html" class="layui-this"><i class="layui-icon layui-icon-home"></i></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<div class="layui-body auto-hide-menu" id="LAY_app_body">
|
||||
<div class="layadmin-tabsbody-item layui-show">
|
||||
<iframe id="defaultIFrame" frameborder="0" class="layadmin-iframe"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 辅助元素,一般用于移动设备下遮罩 -->
|
||||
<div class="layadmin-body-shade" layadmin-event="shade"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="assets/layuiadmin/layui/layui.js"></script>
|
||||
<script>
|
||||
layui.config({
|
||||
base: 'assets/layuiadmin/' //静态资源所在路径
|
||||
}).extend({
|
||||
index: 'lib/index' //主入口模块
|
||||
}).use(['index', 'element', 'restajax', 'datamessage', 'dialog'], function() {
|
||||
var element = layui.element;
|
||||
var $ = layui.$;
|
||||
|
||||
var layer = layui.layer;
|
||||
window.dialog = layui.dialog;
|
||||
window.restAjax = layui.restajax;
|
||||
window.dataMessage = layui.datamessage;
|
||||
|
||||
function changePassword() {
|
||||
top.dialog.open({
|
||||
url: top.restAjax.path('route/user/update-password', []),
|
||||
title: '修改密码',
|
||||
width: '400px',
|
||||
height: '290px',
|
||||
onClose: function() {}
|
||||
});
|
||||
}
|
||||
|
||||
function checkPasswordStatus() {
|
||||
top.restAjax.get(top.restAjax.path('api/user/get-password-status', []), {}, null, function(code, data) {
|
||||
if(data.data == 'change') {
|
||||
changePassword();
|
||||
} else if(data.data == 'remind') {
|
||||
layer.open({
|
||||
title: '警告!',
|
||||
content: '密码已过期,为确保账号安全,请尽快修改密码!',
|
||||
auto: ['100px', '80px'],
|
||||
offset: 'rb'
|
||||
});
|
||||
}
|
||||
}, function(code, data) {
|
||||
top.dialog.message(data.msg);
|
||||
});
|
||||
}
|
||||
checkPasswordStatus();
|
||||
|
||||
$('#LAY-changePassword').on('click', function() {
|
||||
changePassword()
|
||||
});
|
||||
$('#defaultIFrame').attr('src', 'default-home');
|
||||
$('#LAY-logout').on('click', function() {
|
||||
top.dialog.confirm('确认退出?', function() {
|
||||
window.location.href = 'oauth/logout';
|
||||
});
|
||||
});
|
||||
|
||||
/** 左浮动菜单 start **/
|
||||
var hideSideMenuTimeout = null;
|
||||
$('.layui-side-menu-show').on('mouseover', function(event) {
|
||||
$('#sideMenu').removeClass('layui-side-menu-hidden');
|
||||
});
|
||||
$('#sideMenu').on('mouseout', function(event) {
|
||||
if(hideSideMenuTimeout) {
|
||||
return;
|
||||
}
|
||||
hideSideMenuTimeout = setTimeout(function() {
|
||||
$('#sideMenu').addClass('layui-side-menu-hidden')
|
||||
}, 100);
|
||||
});
|
||||
$('#sideMenu').on('mouseover', function(event) {
|
||||
if(hideSideMenuTimeout) {
|
||||
clearTimeout(hideSideMenuTimeout);
|
||||
hideSideMenuTimeout = null;
|
||||
}
|
||||
});
|
||||
/** 左浮动菜单 end **/
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -26,6 +26,12 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
@ -37,6 +43,10 @@
|
||||
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
|
@ -2,13 +2,15 @@ package ink.wgink.login.oauth2.server.config;
|
||||
|
||||
import ink.wgink.login.base.service.user.UserDetailServiceImpl;
|
||||
import ink.wgink.login.oauth2.server.converter.UserAccessTokenConverter;
|
||||
import ink.wgink.login.oauth2.server.service.impl.Oauth2ClientDetailsServiceImpl;
|
||||
import ink.wgink.login.oauth2.server.service.impl.Oauth2ClientTokenServiceImpl;
|
||||
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientDetailsServiceImpl;
|
||||
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientTokenServiceImpl;
|
||||
import ink.wgink.service.user.service.IUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
@ -52,14 +54,18 @@ public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigur
|
||||
@Autowired
|
||||
private IUserService userService;
|
||||
@Autowired
|
||||
private Oauth2ClientDetailsServiceImpl oAuth2ClientDetailsService;
|
||||
private OAuth2ClientDetailsServiceImpl oAuth2ClientDetailsService;
|
||||
@Autowired
|
||||
private OAuth2ClientTokenServiceImpl oAuth2ClientTokenService;
|
||||
@Autowired
|
||||
private Oauth2ClientTokenServiceImpl oAuth2ClientTokenService;
|
||||
private AuthenticationManager authenticationManager;
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
// 通过内存的方式来完成认证服务
|
||||
// 通过业务代码完成认证
|
||||
clients.withClientDetails(oAuth2ClientDetailsService);
|
||||
}
|
||||
|
||||
@ -76,12 +82,12 @@ public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigur
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
// 添加JWT授权机制
|
||||
endpoints
|
||||
.pathMapping("/oauth/authorize", "/oauth_client/authorize")
|
||||
.pathMapping("/oauth/token", "/oauth_client/token")
|
||||
.pathMapping("/oauth/token_key", "/oauth_client/token_key")
|
||||
.pathMapping("/oauth/check_token", "/oauth_client/check_token")
|
||||
.pathMapping("/oauth/confirm_access", "/oauth_client/confirm_access")
|
||||
.pathMapping("/oauth/error", "/oauth_client/error")
|
||||
.pathMapping("/oauth/authorize", "/oauth2_client/authorize")
|
||||
.pathMapping("/oauth/token", "/oauth2_client/token")
|
||||
.pathMapping("/oauth/token_key", "/oauth2_client/token_key")
|
||||
.pathMapping("/oauth/check_token", "/oauth2_client/check_token")
|
||||
.pathMapping("/oauth/confirm_access", "/oauth2_client/confirm_access")
|
||||
.pathMapping("/oauth/error", "/oauth2_client/error")
|
||||
.authenticationManager(authenticationManager)
|
||||
.tokenStore(jwtTokenStore())
|
||||
.accessTokenConverter(jwtAccessTokenConverter())
|
||||
@ -152,5 +158,13 @@ public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigur
|
||||
return tokenStoreUserApprovalHandler;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
PasswordEncoder pe = new BCryptPasswordEncoder();
|
||||
System.out.println(pe.encode("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA=="));
|
||||
// boolean matches = pe.matches("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA==", "$2a$10$.IjNieJzGGpC0Vv3gjIWB.Zjj1pi9VZS8M96zbbv3qj8gDsYX./h6");
|
||||
boolean matches = pe.matches("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA==", "$2a$10$nopzmRLR8HneFyw2uccG9u3XNnK4Cupzb4BR0m/WzSGucB2Rsxwxq");
|
||||
System.out.println(matches);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package ink.wgink.login.oauth2.server.config;
|
||||
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import ink.wgink.login.oauth2.server.converter.OAuth2AccessTokenMessageConverter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassName: OAuth2WebMvcConfigurer
|
||||
* @Description: OAuth2配置,该类是为了处理由于使用了Fastjson而不是原本的Jackson所导致的token格式变化
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/9/18 1:10 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Configuration
|
||||
public class OAuth2WebMvcConfigurer implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.add(0, new FastJsonHttpMessageConverter());
|
||||
converters.add(0, new OAuth2AccessTokenMessageConverter());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package ink.wgink.login.oauth2.server.converter;
|
||||
|
||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @ClassName: OAuth2AccessTokenMessageConverter
|
||||
* @Description: OAuth2的Token解析类
|
||||
* @Author: wanggeng
|
||||
* @Date: 2021/9/18 1:11 下午
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class OAuth2AccessTokenMessageConverter extends AbstractHttpMessageConverter<OAuth2AccessToken> {
|
||||
|
||||
private final FastJsonHttpMessageConverter delegateMessageConverter;
|
||||
|
||||
public OAuth2AccessTokenMessageConverter() {
|
||||
super(MediaType.APPLICATION_JSON);
|
||||
this.delegateMessageConverter = new FastJsonHttpMessageConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supports(Class<?> clazz) {
|
||||
return OAuth2AccessToken.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2AccessToken readInternal(Class<? extends OAuth2AccessToken> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
throw new UnsupportedOperationException("This converter is only used for converting from externally aqcuired form data");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(OAuth2AccessToken accessToken, HttpOutputMessage outputMessage) throws IOException,
|
||||
HttpMessageNotWritableException {
|
||||
Map<String, Object> data = new HashMap<>(8);
|
||||
data.put(OAuth2AccessToken.ACCESS_TOKEN, accessToken.getValue());
|
||||
data.put(OAuth2AccessToken.TOKEN_TYPE, accessToken.getTokenType());
|
||||
data.put(OAuth2AccessToken.EXPIRES_IN, accessToken.getExpiresIn());
|
||||
data.put(OAuth2AccessToken.SCOPE, String.join(" ", accessToken.getScope()));
|
||||
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
|
||||
if (Objects.nonNull(refreshToken)) {
|
||||
data.put(OAuth2AccessToken.REFRESH_TOKEN, refreshToken.getValue());
|
||||
}
|
||||
delegateMessageConverter.write(data, MediaType.APPLICATION_JSON, outputMessage);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ink.wgink.login.oauth2.server.converter;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import ink.wgink.pojo.bos.LoginUser;
|
||||
import ink.wgink.pojo.bos.UserInfoBO;
|
||||
import ink.wgink.service.user.service.IUserService;
|
||||
@ -46,7 +47,7 @@ public class UserAuthConverter implements UserAuthenticationConverter {
|
||||
userInfoBO.setGroups(loginUser.getGroups());
|
||||
userInfoBO.setPositions(loginUser.getPositions());
|
||||
userInfoBO.setExpandData(loginUser.getExpandData());
|
||||
response.put("user_info", userInfoBO);
|
||||
response.put("user_info", JSONObject.toJSONString(userInfoBO));
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,427 @@
|
||||
package ink.wgink.login.oauth2.server.endpoint;
|
||||
|
||||
import ink.wgink.login.oauth2.server.exceptions.OAuth2ClientBadClientCredentialsException;
|
||||
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientDetailsServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.*;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
|
||||
import org.springframework.security.oauth2.provider.endpoint.DefaultRedirectResolver;
|
||||
import org.springframework.security.oauth2.provider.endpoint.RedirectResolver;
|
||||
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
|
||||
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpSessionRequiredException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
* 当你想要放弃的时候,想想当初你为何开始
|
||||
*
|
||||
* @ClassName: OauthClientAuthorizationEndpoint
|
||||
* @Description: Oauth客户端认证端口
|
||||
* @Author: WangGeng
|
||||
* @Date: 2020/7/23 8:25 下午
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@Controller
|
||||
@SessionAttributes("authorizationRequest")
|
||||
public class OAuth2ClientAuthorizationEndpoint extends AbstractEndpoint {
|
||||
|
||||
private RedirectResolver redirectResolver = new DefaultRedirectResolver();
|
||||
|
||||
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||
private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
|
||||
private String userApprovalPage = "error/oauth_confirm_access";
|
||||
private String errorPage = "error/oauth_error";
|
||||
private Object implicitLock = new Object();
|
||||
@Autowired
|
||||
private UserApprovalHandler userApprovalHandler;
|
||||
@Autowired
|
||||
private AuthorizationCodeServices authorizationCodeServices;
|
||||
@Autowired
|
||||
private OAuth2ClientDetailsServiceImpl oauthClientDetailsService;
|
||||
@Autowired
|
||||
private TokenGranter tokenGranter;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
setTokenGranter(tokenGranter);
|
||||
setClientDetailsService(oauthClientDetailsService);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth2_client/authorize")
|
||||
public ModelAndView authorize(Map<String, Object> model,
|
||||
@RequestParam Map<String, String> parameters,
|
||||
SessionStatus sessionStatus,
|
||||
Principal principal) {
|
||||
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
|
||||
Set<String> responseTypes = authorizationRequest.getResponseTypes();
|
||||
if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
|
||||
throw new UnsupportedResponseTypeException("响应类型不支持: " + responseTypes);
|
||||
}
|
||||
if (authorizationRequest.getClientId() == null) {
|
||||
throw new InvalidClientException("客户端不存在。");
|
||||
}
|
||||
try {
|
||||
if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
|
||||
throw new InsufficientAuthenticationException("系统认证完成后才能进行用户认证。");
|
||||
}
|
||||
ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
|
||||
|
||||
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
|
||||
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
|
||||
if (!StringUtils.hasText(resolvedRedirect)) {
|
||||
throw new RedirectMismatchException("客户端必须提供重定向URI。");
|
||||
}
|
||||
authorizationRequest.setRedirectUri(resolvedRedirect);
|
||||
oauth2RequestValidator.validateScope(authorizationRequest, client);
|
||||
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication) principal);
|
||||
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
|
||||
authorizationRequest.setApproved(approved);
|
||||
if (authorizationRequest.isApproved()) {
|
||||
if (responseTypes.contains("token")) {
|
||||
return getImplicitGrantResponse(authorizationRequest);
|
||||
}
|
||||
if (responseTypes.contains("code")) {
|
||||
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
|
||||
(Authentication) principal));
|
||||
}
|
||||
}
|
||||
model.put("authorizationRequest", authorizationRequest);
|
||||
return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
|
||||
} catch (RuntimeException e) {
|
||||
sessionStatus.setComplete();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth2_client/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
|
||||
public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
|
||||
Map<String, ?> model,
|
||||
SessionStatus sessionStatus,
|
||||
Principal principal) {
|
||||
if (!(principal instanceof Authentication)) {
|
||||
sessionStatus.setComplete();
|
||||
throw new InsufficientAuthenticationException(
|
||||
"在授权访问令牌之前,必须使系统对用户进行身份验证。");
|
||||
}
|
||||
AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
|
||||
if (authorizationRequest == null) {
|
||||
sessionStatus.setComplete();
|
||||
throw new InvalidRequestException("无法批准未初始化的授权请求。");
|
||||
}
|
||||
|
||||
try {
|
||||
Set<String> responseTypes = authorizationRequest.getResponseTypes();
|
||||
authorizationRequest.setApprovalParameters(approvalParameters);
|
||||
authorizationRequest = userApprovalHandler.updateAfterApproval(authorizationRequest,
|
||||
(Authentication) principal);
|
||||
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
|
||||
authorizationRequest.setApproved(approved);
|
||||
if (authorizationRequest.getRedirectUri() == null) {
|
||||
sessionStatus.setComplete();
|
||||
throw new InvalidRequestException("未提供用于重定向的URI。");
|
||||
}
|
||||
|
||||
if (!authorizationRequest.isApproved()) {
|
||||
return new RedirectView(getUnsuccessfulRedirect(authorizationRequest,
|
||||
new UserDeniedAuthorizationException("用户拒绝访问。"), responseTypes.contains("token")),
|
||||
false, true, false);
|
||||
}
|
||||
if (responseTypes.contains("token")) {
|
||||
return getImplicitGrantResponse(authorizationRequest).getView();
|
||||
}
|
||||
return getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal);
|
||||
} finally {
|
||||
sessionStatus.setComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
|
||||
AuthorizationRequest authorizationRequest, Authentication principal) {
|
||||
logger.debug("Loading user approval page: " + userApprovalPage);
|
||||
model.putAll(userApprovalHandler.getUserApprovalRequest(authorizationRequest, principal));
|
||||
return new ModelAndView(userApprovalPage, model);
|
||||
}
|
||||
|
||||
private ModelAndView getImplicitGrantResponse(AuthorizationRequest authorizationRequest) {
|
||||
try {
|
||||
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(authorizationRequest, "implicit");
|
||||
OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
|
||||
OAuth2AccessToken accessToken = getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request);
|
||||
if (accessToken == null) {
|
||||
throw new UnsupportedResponseTypeException("不支持的响应类型: token");
|
||||
}
|
||||
return new ModelAndView(new RedirectView(appendAccessToken(authorizationRequest, accessToken), false, true,
|
||||
false));
|
||||
} catch (OAuth2Exception e) {
|
||||
return new ModelAndView(new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, true), false,
|
||||
true, false));
|
||||
}
|
||||
}
|
||||
|
||||
private OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest,
|
||||
OAuth2Request storedOAuth2Request) {
|
||||
OAuth2AccessToken accessToken = null;
|
||||
synchronized (this.implicitLock) {
|
||||
accessToken = getTokenGranter().grant("implicit",
|
||||
new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
|
||||
}
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) {
|
||||
try {
|
||||
return new RedirectView(getSuccessfulRedirect(authorizationRequest,
|
||||
generateCode(authorizationRequest, authUser)), false, true, false);
|
||||
} catch (OAuth2Exception e) {
|
||||
return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, false), false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private String appendAccessToken(AuthorizationRequest authorizationRequest, OAuth2AccessToken accessToken) {
|
||||
Map<String, Object> vars = new LinkedHashMap<>();
|
||||
Map<String, String> keys = new HashMap<>();
|
||||
if (accessToken == null) {
|
||||
throw new InvalidRequestException("无法完成implicit授权");
|
||||
}
|
||||
vars.put("access_token", accessToken.getValue());
|
||||
vars.put("token_type", accessToken.getTokenType());
|
||||
String state = authorizationRequest.getState();
|
||||
if (state != null) {
|
||||
vars.put("state", state);
|
||||
}
|
||||
Date expiration = accessToken.getExpiration();
|
||||
if (expiration != null) {
|
||||
long expires_in = (expiration.getTime() - System.currentTimeMillis()) / 1000;
|
||||
vars.put("expires_in", expires_in);
|
||||
}
|
||||
String originalScope = authorizationRequest.getRequestParameters().get(OAuth2Utils.SCOPE);
|
||||
if (originalScope == null || !OAuth2Utils.parseParameterList(originalScope).equals(accessToken.getScope())) {
|
||||
vars.put("scope", OAuth2Utils.formatParameterList(accessToken.getScope()));
|
||||
}
|
||||
Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
|
||||
for (String key : additionalInformation.keySet()) {
|
||||
Object value = additionalInformation.get(key);
|
||||
if (value != null) {
|
||||
keys.put("extra_" + key, key);
|
||||
vars.put("extra_" + key, value);
|
||||
}
|
||||
}
|
||||
return append(authorizationRequest.getRedirectUri(), vars, keys, true);
|
||||
}
|
||||
|
||||
private String generateCode(AuthorizationRequest authorizationRequest, Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
try {
|
||||
OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
|
||||
OAuth2Authentication combinedAuth = new OAuth2Authentication(storedOAuth2Request, authentication);
|
||||
String code = authorizationCodeServices.createAuthorizationCode(combinedAuth);
|
||||
return code;
|
||||
} catch (OAuth2Exception e) {
|
||||
if (authorizationRequest.getState() != null) {
|
||||
e.addAdditionalInformation("state", authorizationRequest.getState());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getSuccessfulRedirect(AuthorizationRequest authorizationRequest, String authorizationCode) {
|
||||
if (authorizationCode == null) {
|
||||
throw new IllegalStateException("在当前请求范围中找不到授权码");
|
||||
}
|
||||
Map<String, String> query = new LinkedHashMap<>();
|
||||
query.put("code", authorizationCode);
|
||||
String state = authorizationRequest.getState();
|
||||
if (state != null) {
|
||||
query.put("state", state);
|
||||
}
|
||||
return append(authorizationRequest.getRedirectUri(), query, false);
|
||||
}
|
||||
|
||||
private String getUnsuccessfulRedirect(AuthorizationRequest authorizationRequest, OAuth2Exception failure,
|
||||
boolean fragment) {
|
||||
if (authorizationRequest == null || authorizationRequest.getRedirectUri() == null) {
|
||||
throw new UnapprovedClientAuthenticationException("授权失败,并且没有重定向URI。", failure);
|
||||
}
|
||||
|
||||
Map<String, String> query = new LinkedHashMap<>();
|
||||
query.put("error", failure.getOAuth2ErrorCode());
|
||||
query.put("error_description", failure.getMessage());
|
||||
if (authorizationRequest.getState() != null) {
|
||||
query.put("state", authorizationRequest.getState());
|
||||
}
|
||||
if (failure.getAdditionalInformation() != null) {
|
||||
for (Map.Entry<String, String> additionalInfo : failure.getAdditionalInformation().entrySet()) {
|
||||
query.put(additionalInfo.getKey(), additionalInfo.getValue());
|
||||
}
|
||||
}
|
||||
return append(authorizationRequest.getRedirectUri(), query, fragment);
|
||||
}
|
||||
|
||||
private String append(String base, Map<String, ?> query, boolean fragment) {
|
||||
return append(base, query, null, fragment);
|
||||
}
|
||||
|
||||
private String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
|
||||
UriComponentsBuilder template = UriComponentsBuilder.newInstance();
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
|
||||
URI redirectUri;
|
||||
try {
|
||||
redirectUri = builder.build(true).toUri();
|
||||
} catch (Exception e) {
|
||||
redirectUri = builder.build().toUri();
|
||||
builder = UriComponentsBuilder.fromUri(redirectUri);
|
||||
}
|
||||
template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
|
||||
.userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
|
||||
if (fragment) {
|
||||
StringBuilder values = new StringBuilder();
|
||||
if (redirectUri.getFragment() != null) {
|
||||
String append = redirectUri.getFragment();
|
||||
values.append(append);
|
||||
}
|
||||
for (String key : query.keySet()) {
|
||||
if (values.length() > 0) {
|
||||
values.append("&");
|
||||
}
|
||||
String name = key;
|
||||
if (keys != null && keys.containsKey(key)) {
|
||||
name = keys.get(key);
|
||||
}
|
||||
values.append(name + "={" + key + "}");
|
||||
}
|
||||
if (values.length() > 0) {
|
||||
template.fragment(values.toString());
|
||||
}
|
||||
UriComponents encoded = template.build().expand(query).encode();
|
||||
builder.fragment(encoded.getFragment());
|
||||
} else {
|
||||
for (String key : query.keySet()) {
|
||||
String name = key;
|
||||
if (keys != null && keys.containsKey(key)) {
|
||||
name = keys.get(key);
|
||||
}
|
||||
template.queryParam(name, "{" + key + "}");
|
||||
}
|
||||
template.fragment(redirectUri.getFragment());
|
||||
UriComponents encoded = template.build().expand(query).encode();
|
||||
builder.query(encoded.getQuery());
|
||||
}
|
||||
return builder.build().toUriString();
|
||||
}
|
||||
|
||||
public void setUserApprovalPage(String userApprovalPage) {
|
||||
this.userApprovalPage = userApprovalPage;
|
||||
}
|
||||
|
||||
public void setAuthorizationCodeServices(AuthorizationCodeServices authorizationCodeServices) {
|
||||
this.authorizationCodeServices = authorizationCodeServices;
|
||||
}
|
||||
|
||||
public void setRedirectResolver(RedirectResolver redirectResolver) {
|
||||
this.redirectResolver = redirectResolver;
|
||||
}
|
||||
|
||||
public void setUserApprovalHandler(UserApprovalHandler userApprovalHandler) {
|
||||
this.userApprovalHandler = userApprovalHandler;
|
||||
}
|
||||
|
||||
public void setOAuth2RequestValidator(OAuth2RequestValidator oauth2RequestValidator) {
|
||||
this.oauth2RequestValidator = oauth2RequestValidator;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setImplicitGrantService(
|
||||
org.springframework.security.oauth2.provider.implicit.ImplicitGrantService implicitGrantService) {
|
||||
}
|
||||
|
||||
@ExceptionHandler(ClientRegistrationException.class)
|
||||
public ModelAndView handleClientRegistrationException(Exception e, ServletWebRequest webRequest) throws Exception {
|
||||
logger.info("Handling ClientRegistrationException error: " + e.getMessage());
|
||||
return handleException(new OAuth2ClientBadClientCredentialsException(e.getMessage()), webRequest);
|
||||
}
|
||||
|
||||
@ExceptionHandler(OAuth2Exception.class)
|
||||
public ModelAndView handleOAuth2Exception(OAuth2Exception e, ServletWebRequest webRequest) throws Exception {
|
||||
logger.info("Handling OAuth2 error: " + e.getSummary());
|
||||
return handleException(e, webRequest);
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpSessionRequiredException.class)
|
||||
public ModelAndView handleHttpSessionRequiredException(HttpSessionRequiredException e, ServletWebRequest webRequest)
|
||||
throws Exception {
|
||||
logger.info("Handling Session required error: " + e.getMessage());
|
||||
return handleException(new AccessDeniedException("无法从会话获取授权请求。", e),
|
||||
webRequest);
|
||||
}
|
||||
|
||||
private ModelAndView handleException(Exception e, ServletWebRequest webRequest) throws Exception {
|
||||
ResponseEntity<OAuth2Exception> translate = getExceptionTranslator().translate(e);
|
||||
webRequest.getResponse().setStatus(translate.getStatusCode().value());
|
||||
if (e instanceof ClientAuthenticationException || e instanceof RedirectMismatchException) {
|
||||
return new ModelAndView(errorPage, Collections.singletonMap("error", translate.getBody().getMessage()));
|
||||
}
|
||||
AuthorizationRequest authorizationRequest = null;
|
||||
try {
|
||||
authorizationRequest = getAuthorizationRequestForError(webRequest);
|
||||
String requestedRedirectParam = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
|
||||
String requestedRedirect = redirectResolver.resolveRedirect(requestedRedirectParam,
|
||||
getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId()));
|
||||
authorizationRequest.setRedirectUri(requestedRedirect);
|
||||
String redirect = getUnsuccessfulRedirect(authorizationRequest, translate.getBody(), authorizationRequest
|
||||
.getResponseTypes().contains("token"));
|
||||
return new ModelAndView(new RedirectView(redirect, false, true, false));
|
||||
} catch (OAuth2Exception ex) {
|
||||
return new ModelAndView(errorPage, Collections.singletonMap("error", translate.getBody().getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest webRequest) {
|
||||
AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
|
||||
webRequest, "authorizationRequest");
|
||||
if (authorizationRequest != null) {
|
||||
return authorizationRequest;
|
||||
}
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
Map<String, String[]> map = webRequest.getParameterMap();
|
||||
for (String key : map.keySet()) {
|
||||
String[] values = map.get(key);
|
||||
if (values != null && values.length > 0) {
|
||||
parameters.put(key, values[0]);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return getOAuth2RequestFactory().createAuthorizationRequest(parameters);
|
||||
} catch (Exception e) {
|
||||
return getDefaultOAuth2RequestFactory().createAuthorizationRequest(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package ink.wgink.login.oauth2.server.endpoint;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
|
||||
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
* 当你想要放弃的时候,想想当初你为何开始
|
||||
*
|
||||
* @ClassName: OauthClientCheckTokenEndpoint
|
||||
* @Description: Oauth客户端token校验
|
||||
* @Author: WangGeng
|
||||
* @Date: 2020/7/24 11:10 上午
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@Controller
|
||||
public class OAuth2ClientCheckTokenEndpoint {
|
||||
|
||||
private ResourceServerTokenServices resourceServerTokenServices;
|
||||
|
||||
private AccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();
|
||||
|
||||
public OAuth2ClientCheckTokenEndpoint(ResourceServerTokenServices resourceServerTokenServices) {
|
||||
this.resourceServerTokenServices = resourceServerTokenServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param exceptionTranslator the exception translator to set
|
||||
*/
|
||||
public void setExceptionTranslator(WebResponseExceptionTranslator exceptionTranslator) {
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accessTokenConverter the accessTokenConverter to set
|
||||
*/
|
||||
public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) {
|
||||
this.accessTokenConverter = accessTokenConverter;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth2_client/check_token")
|
||||
@ResponseBody
|
||||
public Map<String, ?> checkToken(@RequestParam("token") String value) {
|
||||
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
|
||||
if (token == null) {
|
||||
throw new InvalidTokenException("Token was not recognised");
|
||||
}
|
||||
if (token.isExpired()) {
|
||||
throw new InvalidTokenException("Token has expired");
|
||||
}
|
||||
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
|
||||
Map<String, Object> response = (Map<String, Object>) accessTokenConverter.convertAccessToken(token, authentication);
|
||||
response.put("active", true);
|
||||
return response;
|
||||
}
|
||||
|
||||
@ExceptionHandler(InvalidTokenException.class)
|
||||
public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
|
||||
logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
|
||||
@SuppressWarnings("serial")
|
||||
InvalidTokenException e400 = new InvalidTokenException(e.getMessage()) {
|
||||
@Override
|
||||
public int getHttpErrorCode() {
|
||||
return 400;
|
||||
}
|
||||
};
|
||||
return exceptionTranslator.translate(e400);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
package ink.wgink.login.oauth2.server.endpoint;
|
||||
|
||||
import ink.wgink.login.oauth2.server.exceptions.OAuth2ClientBadClientCredentialsException;
|
||||
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientDetailsServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.*;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
|
||||
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
* 当你想要放弃的时候,想想当初你为何开始
|
||||
*
|
||||
* @ClassName: OauthClientTokenEndpoint
|
||||
* @Description: 客户端Token端口
|
||||
* @Author: WangGeng
|
||||
* @Date: 2020/7/24 10:06 上午
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@Controller
|
||||
public class OAuth2ClientTokenEndpoint extends AbstractEndpoint {
|
||||
|
||||
private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
|
||||
private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));
|
||||
@Autowired
|
||||
private TokenGranter tokenGranter;
|
||||
@Autowired
|
||||
private OAuth2ClientDetailsServiceImpl oauthClientDetailsService;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
setTokenGranter(tokenGranter);
|
||||
setClientDetailsService(oauthClientDetailsService);
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth2_client/token", method = RequestMethod.GET)
|
||||
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal,
|
||||
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
|
||||
if (!allowedRequestMethods.contains(HttpMethod.GET)) {
|
||||
throw new HttpRequestMethodNotSupportedException("GET");
|
||||
}
|
||||
return postAccessToken(principal, parameters);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/oauth2_client/token", method = RequestMethod.POST)
|
||||
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal,
|
||||
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
|
||||
if (!(principal instanceof Authentication)) {
|
||||
throw new InsufficientAuthenticationException("无客户端身份验证。尝试添加适当的身份验证筛选器。");
|
||||
}
|
||||
String clientId = getClientId(principal);
|
||||
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
|
||||
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
|
||||
if (clientId != null && !clientId.equals("")) {
|
||||
if (!clientId.equals(tokenRequest.getClientId())) {
|
||||
throw new InvalidClientException("给定的客户端ID与经过身份验证的客户端不匹配。");
|
||||
}
|
||||
}
|
||||
if (authenticatedClient != null) {
|
||||
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
|
||||
}
|
||||
if (!StringUtils.hasText(tokenRequest.getGrantType())) {
|
||||
throw new InvalidRequestException("缺少授权类型。");
|
||||
}
|
||||
if (tokenRequest.getGrantType().equals("implicit")) {
|
||||
throw new InvalidGrantException("令牌终结点不支持Implicit授予类型");
|
||||
}
|
||||
if (isAuthCodeRequest(parameters)) {
|
||||
if (!tokenRequest.getScope().isEmpty()) {
|
||||
logger.debug("Clearing scope of incoming token request");
|
||||
tokenRequest.setScope(Collections.<String>emptySet());
|
||||
}
|
||||
}
|
||||
if (isRefreshTokenRequest(parameters)) {
|
||||
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
|
||||
}
|
||||
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
|
||||
if (token == null) {
|
||||
throw new UnsupportedGrantTypeException("不支持的授权类型: " + tokenRequest.getGrantType());
|
||||
}
|
||||
|
||||
return getResponse(token);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param principal the currently authentication principal
|
||||
* @return a client id if there is one in the principal
|
||||
*/
|
||||
protected String getClientId(Principal principal) {
|
||||
Authentication client = (Authentication) principal;
|
||||
if (!client.isAuthenticated()) {
|
||||
throw new InsufficientAuthenticationException("客户端未经过身份验证。");
|
||||
}
|
||||
String clientId = client.getName();
|
||||
if (client instanceof OAuth2Authentication) {
|
||||
clientId = ((OAuth2Authentication) client).getOAuth2Request().getClientId();
|
||||
}
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<OAuth2Exception> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage(), e);
|
||||
}
|
||||
return getExceptionTranslator().translate(e);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage(), e);
|
||||
}
|
||||
return getExceptionTranslator().translate(e);
|
||||
}
|
||||
|
||||
@ExceptionHandler(ClientRegistrationException.class)
|
||||
public ResponseEntity<OAuth2Exception> handleClientRegistrationException(Exception e) throws Exception {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage(), e);
|
||||
}
|
||||
return getExceptionTranslator().translate(new OAuth2ClientBadClientCredentialsException(e.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(OAuth2Exception.class)
|
||||
public ResponseEntity<OAuth2Exception> handleException(OAuth2Exception e) throws Exception {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage(), e);
|
||||
}
|
||||
return getExceptionTranslator().translate(e);
|
||||
}
|
||||
|
||||
private ResponseEntity<OAuth2AccessToken> getResponse(OAuth2AccessToken accessToken) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Cache-Control", "no-store");
|
||||
headers.set("Pragma", "no-cache");
|
||||
return new ResponseEntity<OAuth2AccessToken>(accessToken, headers, HttpStatus.OK);
|
||||
}
|
||||
|
||||
private boolean isRefreshTokenRequest(Map<String, String> parameters) {
|
||||
return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null;
|
||||
}
|
||||
|
||||
private boolean isAuthCodeRequest(Map<String, String> parameters) {
|
||||
return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null;
|
||||
}
|
||||
|
||||
public void setOAuth2RequestValidator(OAuth2RequestValidator oAuth2RequestValidator) {
|
||||
this.oAuth2RequestValidator = oAuth2RequestValidator;
|
||||
}
|
||||
|
||||
public void setAllowedRequestMethods(Set<HttpMethod> allowedRequestMethods) {
|
||||
this.allowedRequestMethods = allowedRequestMethods;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package ink.wgink.login.oauth2.server.endpoint;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
* 当你想要放弃的时候,想想当初你为何开始
|
||||
*
|
||||
* @ClassName: OauthClientTokenKeyEndpoint
|
||||
* @Description: Oauth客户端TokenKey端口
|
||||
* @Author: WangGeng
|
||||
* @Date: 2020/7/26 9:59 上午
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@Controller
|
||||
public class OAuth2ClientTokenKeyEndpoint {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Resource(name = "jwtAccessTokenConverter")
|
||||
private JwtAccessTokenConverter converter;
|
||||
|
||||
/**
|
||||
* Get the verification key for the token signatures. The principal has to
|
||||
* be provided only if the key is secret
|
||||
* (shared not public).
|
||||
*
|
||||
* @param principal the currently authenticated user if there is one
|
||||
* @return the key used to verify tokens
|
||||
*/
|
||||
@RequestMapping(value = "/oauth2_client/token_key", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Map<String, String> getKey(Principal principal) {
|
||||
boolean flag = (principal == null || principal instanceof AnonymousAuthenticationToken) && !converter.isPublic();
|
||||
if (flag) {
|
||||
throw new AccessDeniedException("您需要进行身份验证才能查看共享密钥");
|
||||
}
|
||||
Map<String, String> result = converter.getKey();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package ink.wgink.login.oauth2.server.pojo.pos;
|
||||
|
||||
import ink.wgink.login.oauth2.server.pojo.dtos.OAuth2ClientSimpleDTO;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @ClassName: OauthClientDTO
|
||||
@ -10,9 +9,12 @@ import io.swagger.annotations.ApiModel;
|
||||
* @Date: 2019/1/8 7:43 PM
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@ApiModel
|
||||
public class OAuth2ClientPO extends OAuth2ClientSimpleDTO {
|
||||
public class OAuth2ClientPO implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 2201386758342553263L;
|
||||
private String clientId;
|
||||
private String clientName;
|
||||
private String webServerRedirectUri;
|
||||
private String resourceIds;
|
||||
private String clientSecret;
|
||||
private String scope;
|
||||
@ -35,6 +37,30 @@ public class OAuth2ClientPO extends OAuth2ClientSimpleDTO {
|
||||
private String modifier;
|
||||
private Integer isDelete;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId == null ? "" : clientId.trim();
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientName() {
|
||||
return clientName == null ? "" : clientName.trim();
|
||||
}
|
||||
|
||||
public void setClientName(String clientName) {
|
||||
this.clientName = clientName;
|
||||
}
|
||||
|
||||
public String getWebServerRedirectUri() {
|
||||
return webServerRedirectUri == null ? "" : webServerRedirectUri.trim();
|
||||
}
|
||||
|
||||
public void setWebServerRedirectUri(String webServerRedirectUri) {
|
||||
this.webServerRedirectUri = webServerRedirectUri;
|
||||
}
|
||||
|
||||
public String getResourceIds() {
|
||||
return resourceIds == null ? "" : resourceIds.trim();
|
||||
}
|
||||
@ -163,12 +189,10 @@ public class OAuth2ClientPO extends OAuth2ClientSimpleDTO {
|
||||
this.systemIcon = systemIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGmtCreate() {
|
||||
return gmtCreate == null ? "" : gmtCreate.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGmtCreate(String gmtCreate) {
|
||||
this.gmtCreate = gmtCreate;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public interface IOAuth2ClientService {
|
||||
/**
|
||||
* 客户端加密规则
|
||||
*/
|
||||
String OAUTH_CLIENT_RULE = "WGINK_OAUTH2_CLIENT";
|
||||
String OAUTH_CLIENT_RULE = "WGINK_oauth2";
|
||||
/**
|
||||
* 正常环境
|
||||
*/
|
||||
|
@ -19,9 +19,8 @@ import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* When you feel like quitting. Think about why you started
|
||||
@ -35,8 +34,8 @@ import java.util.Set;
|
||||
**/
|
||||
@Primary
|
||||
@Component
|
||||
public class Oauth2ClientDetailsServiceImpl implements ClientDetailsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Oauth2ClientDetailsServiceImpl.class);
|
||||
public class OAuth2ClientDetailsServiceImpl implements ClientDetailsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OAuth2ClientDetailsServiceImpl.class);
|
||||
@Autowired
|
||||
private IOAuth2ClientService oAuth2ClientService;
|
||||
|
||||
@ -72,7 +71,7 @@ public class Oauth2ClientDetailsServiceImpl implements ClientDetailsService {
|
||||
oAuth2ClientPO.getScope(),
|
||||
oAuth2ClientPO.getAuthorizedGrantTypes(),
|
||||
oAuth2ClientPO.getAuthorities());
|
||||
|
||||
clientDetails.setAutoApproveScopes(Arrays.asList(oAuth2ClientPO.getAutoapprove().split(",")));
|
||||
clientDetails.setClientSecret(oAuth2ClientPO.getClientSecret());
|
||||
clientDetails.setAccessTokenValiditySeconds(oAuth2ClientPO.getAccessTokenValidity());
|
||||
clientDetails.setRefreshTokenValiditySeconds(oAuth2ClientPO.getRefreshTokenValidity());
|
||||
@ -91,14 +90,6 @@ public class Oauth2ClientDetailsServiceImpl implements ClientDetailsService {
|
||||
if (!StringUtils.isBlank(oAuth2ClientPO.getScope())) {
|
||||
clientDetails.setScope(org.springframework.util.StringUtils.commaDelimitedListToSet(oAuth2ClientPO.getScope()));
|
||||
}
|
||||
|
||||
if (clientDetails.isAutoApprove(oAuth2ClientPO.getAutoapprove())) {
|
||||
Set<String> autoApproveScopesSet = new HashSet<>();
|
||||
autoApproveScopesSet.add("true");
|
||||
clientDetails.setAutoApproveScopes(autoApproveScopesSet);
|
||||
} else {
|
||||
clientDetails.setAutoApproveScopes(clientDetails.getScope());
|
||||
}
|
||||
return clientDetails;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import java.util.UUID;
|
||||
* @Version: 1.0
|
||||
**/
|
||||
@Component
|
||||
public class Oauth2ClientTokenServiceImpl implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean {
|
||||
public class OAuth2ClientTokenServiceImpl implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean {
|
||||
|
||||
private int refreshTokenValiditySeconds = 7200;
|
||||
private int accessTokenValiditySeconds = 7200;
|
||||
@ -46,7 +46,7 @@ public class Oauth2ClientTokenServiceImpl implements AuthorizationServerTokenSer
|
||||
private TokenEnhancer accessTokenEnhancer;
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
public Oauth2ClientTokenServiceImpl() {
|
||||
public OAuth2ClientTokenServiceImpl() {
|
||||
}
|
||||
|
||||
@Override
|
@ -53,10 +53,10 @@
|
||||
<result property="systemSummary" column="system_summary"/>
|
||||
<result property="systemIcon" column="system_icon"/>
|
||||
<result property="gmtCreate" column="gmt_create"/>
|
||||
<result property="creator" column="gmt_create"/>
|
||||
<result property="creator" column="creator"/>
|
||||
<result property="gmtModified" column="gmt_create"/>
|
||||
<result property="modifier" column="gmt_create"/>
|
||||
<result property="isDelete" column="gmt_create"/>
|
||||
<result property="modifier" column="modifier"/>
|
||||
<result property="isDelete" column="is_delete"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 建表 -->
|
||||
@ -82,10 +82,10 @@
|
||||
`system_summary` text COMMENT '系统介绍',
|
||||
`system_icon` varchar(255) DEFAULT NULL COMMENT '系统图标',
|
||||
`gmt_create` datetime DEFAULT NULL,
|
||||
`creator` bigint(20) DEFAULT NULL,
|
||||
`creator` char(36) DEFAULT NULL,
|
||||
`gmt_modified` datetime DEFAULT NULL,
|
||||
`modifier` bigint(20) DEFAULT NULL,
|
||||
`is_delete` int(2) DEFAULT '0',
|
||||
`modifier` char(36) DEFAULT NULL,
|
||||
`is_delete` int(1) DEFAULT '0',
|
||||
PRIMARY KEY (`client_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
</update>
|
@ -204,7 +204,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
top.restAjax.get(top.restAjax.path('api/file/listfilebyfileid', []), {
|
||||
top.restAjax.get(top.restAjax.path('api/file/list', []), {
|
||||
ids: ids
|
||||
}, null, function(code, data) {
|
||||
refreshDownloadTemplet(fileName, data);
|
||||
@ -285,7 +285,7 @@
|
||||
// 初始化
|
||||
function initData() {
|
||||
var loadLayerIndex;
|
||||
top.restAjax.get(top.restAjax.path('api/oauthclient/getinitclient', []), {}, null, function(code, data) {
|
||||
top.restAjax.get(top.restAjax.path('api/oauth2client/get-init', []), {}, null, function(code, data) {
|
||||
form.val('dataForm', {
|
||||
clientId: data.clientId,
|
||||
clientSecret: data.clientSecret,
|
||||
@ -310,7 +310,7 @@
|
||||
top.dialog.confirm(top.dataMessage.commit, function(index) {
|
||||
top.dialog.close(index);
|
||||
var loadLayerIndex;
|
||||
top.restAjax.post(top.restAjax.path('api/oauthclient/saveoauthclient', []), formData.field, null, function(code, data) {
|
||||
top.restAjax.post(top.restAjax.path('api/oauth2client/save', []), formData.field, null, function(code, data) {
|
||||
var layerIndex = top.dialog.msg(top.dataMessage.commitSuccess, {
|
||||
time: 0,
|
||||
btn: [top.dataMessage.button.yes, top.dataMessage.button.no],
|
||||
|
@ -205,7 +205,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
top.restAjax.get(top.restAjax.path('api/file/listfilebyfileid', []), {
|
||||
top.restAjax.get(top.restAjax.path('api/file/list', []), {
|
||||
ids: ids
|
||||
}, null, function(code, data) {
|
||||
refreshDownloadTemplet(fileName, data);
|
||||
@ -286,7 +286,7 @@
|
||||
// 初始化
|
||||
function initData() {
|
||||
var loadLayerIndex;
|
||||
top.restAjax.get(top.restAjax.path('api/oauthclient/getoauthclient/{clientId}', [clientId]), {}, null, function(code, data) {
|
||||
top.restAjax.get(top.restAjax.path('api/oauth2client/get/{clientId}', [clientId]), {}, null, function(code, data) {
|
||||
form.val('dataForm', {
|
||||
clientName: data.clientName,
|
||||
clientId: data.clientId,
|
||||
@ -330,7 +330,7 @@
|
||||
top.dialog.confirm(top.dataMessage.commit, function(index) {
|
||||
top.dialog.close(index);
|
||||
var loadLayerIndex;
|
||||
top.restAjax.put(top.restAjax.path('api/oauthclient/updateoauthclient/{clientId}', [clientId]), formData.field, null, function(code, data) {
|
||||
top.restAjax.put(top.restAjax.path('api/oauth2client/update/{clientId}', [clientId]), formData.field, null, function(code, data) {
|
||||
var layerIndex = top.dialog.msg(top.dataMessage.commitSuccess, {
|
||||
time: 0,
|
||||
btn: [top.dataMessage.button.yes, top.dataMessage.button.no],
|
||||
|
12
pom.xml
12
pom.xml
@ -44,7 +44,7 @@
|
||||
|
||||
<properties>
|
||||
<spring.version>5.2.8.RELEASE</spring.version>
|
||||
<spring-security.version>5.5.2</spring-security.version>
|
||||
<spring-security.version>5.1.3.RELEASE</spring-security.version>
|
||||
<spring-boot.version>2.3.3.RELEASE</spring-boot.version>
|
||||
<fastjson.version>1.2.24</fastjson.version>
|
||||
<json.version>20210307</json.version>
|
||||
@ -153,16 +153,6 @@
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-core</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-jose</artifactId>
|
||||
<version>${spring-security.version}</version>
|
||||
</dependency>
|
||||
<!-- Spring end -->
|
||||
|
||||
<!-- springboot start -->
|
||||
|
Loading…
Reference in New Issue
Block a user