oauth server 认证模块
diff --git a/build.gradle b/build.gradle
index b11247b..e318a0d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,8 +30,7 @@
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-oauth2-client'
implementation 'org.springframework.security:spring-security-oauth2-jose'
- implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.1.RELEASE'
-// implementation 'org.springframework.security.oauth:spring-security-oauth2'
+ implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.5.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.session:spring-session-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-cache'
diff --git a/config/application-devel-pg.properties b/config/application-devel-pg.properties
index 5bbedd3..3c76bf5 100644
--- a/config/application-devel-pg.properties
+++ b/config/application-devel-pg.properties
@@ -19,4 +19,5 @@
# timeout seconds
jwt.expiration=3600
auth.password.bcrypt.seed=
-spring.jackson.serialization.fail-on-empty-beans=false
\ No newline at end of file
+spring.jackson.serialization.fail-on-empty-beans=false
+logging.level.org.springframework.web=DEBUG
\ No newline at end of file
diff --git a/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java b/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
old mode 100755
new mode 100644
index 77242b9..bfa9047
--- a/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
@@ -48,11 +48,11 @@
try {
validate(request);
} catch (ValidateCodeException e) {
- response.setStatus(HttpStatus.OK.value());
- response.setContentType("application/json;charset=UTF-8");
- response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));
+ //response.setStatus(HttpStatus.OK.value());
+ //response.setContentType("application/json;charset=UTF-8");
+ //response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, e.getMessage())));
//response.sendError(HttpStatus.UNAUTHORIZED.value(),e.getMessage());
- //myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
+ myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
return;
}
}
diff --git a/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationFailureHandler.java b/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationFailureHandler.java
index bba039e..e090b63 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationFailureHandler.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationFailureHandler.java
@@ -37,9 +37,11 @@
} else if (exception instanceof ValidateCodeException) {
errmsg = exception.getMessage();
}
- response.setStatus(HttpStatus.OK.value());
+ setDefaultFailureUrl("/login");
+ super.onAuthenticationFailure(request, response, new ValidateCodeException(errmsg));
+ /*response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
- response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, errmsg)));
+ response.getWriter().write(objectMapper.writeValueAsString(JsonResult.error(400, errmsg)));*/
}
}
diff --git a/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationSuccessHandler.java b/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationSuccessHandler.java
index f74d15d..abaa46a 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationSuccessHandler.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/security/MyAuthenticationSuccessHandler.java
@@ -5,6 +5,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/com/supwisdom/dlpay/framework/security/ValidateCodeSecurityConfig.java b/src/main/java/com/supwisdom/dlpay/framework/security/ValidateCodeSecurityConfig.java
index 56782db..3941628 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/security/ValidateCodeSecurityConfig.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/security/ValidateCodeSecurityConfig.java
@@ -11,7 +11,7 @@
@Component("validateCodeSecurityConfig")
public class ValidateCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity> {
-
+
@Autowired
private Filter validateCodeFilter;
diff --git a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
index 10f6603..5fb889e 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
@@ -21,18 +21,20 @@
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
+import org.springframework.security.core.Authentication
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
+import org.springframework.security.oauth2.provider.OAuth2Authentication
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler
import org.springframework.social.connect.web.HttpSessionSessionStrategy
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
-import org.springframework.web.bind.annotation.GetMapping
-import org.springframework.web.bind.annotation.PathVariable
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.bind.annotation.*
import org.springframework.web.context.request.ServletWebRequest
+import java.io.IOException
+import java.security.Principal
import java.util.*
import javax.imageio.ImageIO
import javax.servlet.http.HttpServletRequest
@@ -173,6 +175,16 @@
}
}
+@RestController
+class UserInforController {
+
+ @RequestMapping("/api/userinfor")
+ fun user(user:Principal ): Principal {
+ System.out.println(user)
+ return user
+ }
+}
+
@Controller
class WebMainController {
@@ -184,8 +196,24 @@
private val logger = KotlinLogging.logger {}
@GetMapping("/login")
- fun loginView() = "login"
+ fun loginView() = "ologin"
+ @RequestMapping("/third/logout")
+ fun oauthLogout(request:HttpServletRequest,response:HttpServletResponse ) {
+ val back = request.getParameter("redirect_uri")
+ SecurityContextLogoutHandler().logout(request, null, null);
+ try {
+ SecurityContextHolder.getContext().authentication = null
+ if(back!=null){
+ response.sendRedirect(back)
+ }else{
+ logger.debug { request.getHeader("referer") }
+ response.sendRedirect(request.getHeader("referer"))
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
@GetMapping("/logout")
fun logout(request: HttpServletRequest, response: HttpServletResponse): String {
SecurityContextHolder.getContext().authentication?.also {
@@ -203,6 +231,7 @@
model.addAttribute("payapiVersion", commonService.getSystemVersion())
return "index"
}
+
}
@Controller
diff --git a/src/main/kotlin/com/supwisdom/dlpay/oauth.kt b/src/main/kotlin/com/supwisdom/dlpay/oauth.kt
index 6f310a5..d46342c 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/oauth.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/oauth.kt
@@ -1,9 +1,13 @@
package com.supwisdom.dlpay
+import com.supwisdom.dlpay.system.service.ParamService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.security.authentication.AuthenticationManager
+import org.springframework.security.core.Authentication
+import org.springframework.security.core.GrantedAuthority
+import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
@@ -14,28 +18,50 @@
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer
import org.springframework.security.oauth2.provider.ClientDetails
import org.springframework.security.oauth2.provider.ClientDetailsService
+import org.springframework.security.oauth2.provider.client.BaseClientDetails
+import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore
+import java.util.*
+import org.springframework.security.oauth2.provider.token.TokenStore
+
+
class OAuthDetailService : ClientDetailsService {
-
+ @Autowired
+ private lateinit var paramService: ParamService;
override fun loadClientByClientId(clientId: String?): ClientDetails {
- TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ val details = BaseClientDetails()
+ if (clientId.isNullOrEmpty()) {
+ return details
+ }
+ details.clientId = clientId
+ paramService.getApiClientByAppid(clientId)?.let {
+ details.setAuthorizedGrantTypes(Arrays.asList("password","authorization_code","refresh_token"))
+ details.setScope(Arrays.asList("read"))
+ details.setResourceIds(Arrays.asList("oauth2-resource"))
+ val authorities = HashSet<GrantedAuthority>()
+ authorities.add(SimpleGrantedAuthority("ROLE_THIRD_ADMIN"))
+ details.authorities = authorities
+ details.setAutoApproveScopes(Arrays.asList("true"))
+ details.clientSecret = it.secret
+ details.accessTokenValiditySeconds = 3600
+ details.refreshTokenValiditySeconds=43200
+ val redir = HashSet<String>()
+ redir.add("http://localhost:8091/wt/sso/login")
+ details.registeredRedirectUri = redir
+ }
+ return details
}
}
+
@Configuration
class OAuth2Config {
- @Configuration
- @EnableResourceServer
- class ResourceServerConfiguration : ResourceServerConfigurerAdapter() {
- override fun configure(resources: ResourceServerSecurityConfigurer?) {
- resources?.resourceId("payapi")?.stateless(true)
- }
- }
@Configuration
@EnableAuthorizationServer
+
class AuthorizationServerConfigure : AuthorizationServerConfigurerAdapter() {
@Autowired
@@ -44,6 +70,7 @@
@Autowired
private lateinit var authenticationManager: AuthenticationManager
+
override fun configure(security: AuthorizationServerSecurityConfigurer?) {
security?.allowFormAuthenticationForClients()
}
diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt
index 30c0edd..1a9cd86 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/security.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt
@@ -15,6 +15,7 @@
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
+import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.http.HttpStatus
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
@@ -28,6 +29,8 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
+import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
@@ -35,7 +38,10 @@
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import org.springframework.stereotype.Component
+import org.springframework.util.StringUtils
import org.springframework.web.filter.OncePerRequestFilter
+import org.springframework.web.servlet.DispatcherServlet
+import org.springframework.web.servlet.view.RedirectView
import java.security.SecureRandom
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
@@ -53,6 +59,9 @@
private var jwtUtil: JwtTokenUtil? = null
+ @Autowired
+ private lateinit var redisConnectionFactory: RedisConnectionFactory
+
private fun getUtil(): JwtTokenUtil {
if (jwtUtil == null) {
@@ -62,6 +71,23 @@
}
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
+ var context: String? = request.contextPath
+ if (context == null || "" == context.trim { it <= ' ' }) {
+ context = "/"
+ }
+ if(request.requestURI.isEmpty()){
+ filterChain.doFilter(request, response)
+ return
+ }
+ var url = request.requestURI
+ if ("/" != context) {
+ url = url.replace(context,"")
+ }
+ logger.info(url)
+ if(!url.startsWith("/api/")){
+ filterChain.doFilter(request, response)
+ return
+ }
request.getHeader(jwtConfig.header)?.let { authHeader ->
try {
val jwt = if (authHeader.startsWith(jwtConfig.tokenHeader)) {
@@ -69,6 +95,11 @@
} else {
throw JoseException("JWT Header error")
}
+ if(url.contains("/userinfor")){
+ SecurityContextHolder.getContext().authentication = RedisTokenStore(redisConnectionFactory).readAuthentication(jwt)
+ filterChain.doFilter(request, response)
+ return
+ }
val claims = getUtil().verifyToken(jwt)
apiJwtRepository.findById(claims["jti"].toString()).let {
if (!it.isPresent) {
@@ -118,12 +149,13 @@
// 设置 API 访问权限管理
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
+ .antMatcher("/api/**")
.addFilterAfter(apiFilter,
UsernamePasswordAuthenticationFilter::class.java)
- .antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/notify/**").permitAll()
+ .antMatchers("/api/userinfor").hasAnyRole("ADMIN","THIRD_ADMIN")
.antMatchers("/api/common/**").hasAnyRole("THIRD_COMMON", "THIRD_ADMIN")
.antMatchers("/api/consume/**").hasRole("THIRD_CONSUME")
.antMatchers("/api/deposit/**").hasRole("THIRD_DEPOSIT")
@@ -131,7 +163,7 @@
.antMatchers("/api/shop/**").hasRole("THIRD_SHOP")
.anyRequest().hasRole("THIRD_COMMON")
.and()
- .csrf().ignoringAntMatchers("/api/**")
+ .csrf().ignoringAntMatchers("/api/**","oauth/**")
}
@Bean
@@ -206,15 +238,15 @@
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/form")
- .successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
+ .defaultSuccessUrl("/",false)
.and()
.logout()
.logoutRequestMatcher(AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
- .and().csrf()
+ .and().csrf().ignoringAntMatchers("oauth/**")
// 设置 Web MVC 应用权限
// http.apply(validateCodeSecurityConfig)
// .and()
diff --git a/src/main/resources/templates/ologin.html b/src/main/resources/templates/ologin.html
new file mode 100644
index 0000000..41302e1
--- /dev/null
+++ b/src/main/resources/templates/ologin.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+
+<html xmlns:th="http://www.thymeleaf.org">
+
+<head>
+ <title>统一身份认证</title>
+ <meta name="_csrf" th:content="${_csrf.token}"/>
+ <!-- default header name is X-CSRF-TOKEN -->
+ <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/static/libs/layui/css/layui.css}"/>
+ <link rel="stylesheet" type="text/css" th:href="@{/static/custom/css/login.css}"/>
+</head>
+<body>
+<div class="login-wrapper">
+ <div class=" login-body">
+ <div class="layui-card">
+ <div class="layui-card-header">
+ <i class="layui-icon layui-icon-engine"></i> 统一身份认证
+ </div>
+ <div class="layui-card-body layui-form layui-form-pane">
+ <p style="color:red;padding: 0 0 10px 0;" th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']!=null and session['SPRING_SECURITY_LAST_EXCEPTION'].message!=null}" th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}"></p>
+ <form th:action="@{/login/form}" method="post">
+ <div class="layui-form-item">
+ <label class="layui-form-label"><i class="layui-icon layui-icon-username"></i></label>
+ <div class="layui-input-block">
+ <input name="username" type="text" lay-verify="required" placeholder="账号"
+ class="layui-input">
+ <input type="hidden"
+ th:name="${_csrf.parameterName}"
+ th:value="${_csrf.token}"/>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><i class="layui-icon layui-icon-password"></i></label>
+ <div class="layui-input-block">
+ <input name="password" type="password" lay-verify="required" placeholder="密码"
+ class="layui-input">
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <label class="layui-form-label"><i class="layui-icon layui-icon-vercode"></i></label>
+ <div class="layui-input-block">
+ <div class="layui-row inline-block">
+ <div class="layui-col-xs7">
+ <input name="imageCode" type="text" placeholder="验证码"
+ class="layui-input">
+ </div>
+ <div class="layui-col-xs5" style="padding-left: 10px;">
+ <img width="100px" height="35px" class="login-captcha" th:src="@{/code/image}">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="layui-form-item">
+ <!--<a href="javascript:;" class="layui-link">帐号注册</a>-->
+ <a href="javascript:;" class="layui-link pull-right">忘记密码?</a>
+ </div>
+ <div class="layui-form-item">
+ <button type="submit" lay-filter="login-submit" class="layui-btn layui-btn-fluid" lay-submit>登 录</button>
+ </div>
+ </form>
+ <!--<div class="layui-form-item login-other">-->
+ <!--<label>第三方登录</label>-->
+ <!--<a href="javascript:;"><i class="layui-icon layui-icon-login-qq"></i></a>-->
+ <!--<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat"></i></a>-->
+ <!--<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo"></i></a>-->
+ <!--</div>-->
+ </div>
+ </div>
+ </div>
+
+ <div class="login-footer">
+ <p>© 2019 <a href="javascript:;" target="_blank">上海树维信息科技有限公司 版权所有</a></p>
+ <!--<p>-->
+ <!--<span><a href="javascript:;" target="_blank">前往github</a></span>-->
+ <!--<span><a href="https://gitee.com/andyzy/zy-admin.git" target="_blank">前往gitee</a></span>-->
+ <!--<span><a href="javascript:;" target="_blank">文档说明</a></span>-->
+ <!--</p>-->
+ </div>
+</div>
+
+<script type="text/javascript" th:src="@{/static/libs/layui/layui.js}" ></script>
+<script>
+ layui.config({
+ base: 'custom/module/'
+ }).use(['form'], function () {
+ var $ = layui.jquery;
+ });
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/templates/system/operator/operator.html b/src/main/resources/templates/system/operator/operator.html
index a048c1a..74bc462 100644
--- a/src/main/resources/templates/system/operator/operator.html
+++ b/src/main/resources/templates/system/operator/operator.html
@@ -16,6 +16,12 @@
</div>
</div>
<div class="layui-form-item">
+ <label class="layui-form-label" title="用于第三方授权登录,但不能访问本系统资源">授权登录用户</label>
+ <div class="layui-input-block">
+ <input type="checkbox" name="thirdadmin" value="yes" />
+ </div>
+ </div>
+ <div class="layui-form-item">
<label class="layui-form-label">性别</label>
<div class="layui-input-block">
<input type="radio" name="sex" value="male" title="男" checked/>