基本测试API认证,WebApp 认证,CSRF模块
authorTang Cheng <cheng.tang@supwisdom.com>
Mon, 22 Apr 2019 05:07:00 +0000 (13:07 +0800)
committerTang Cheng <cheng.tang@supwisdom.com>
Mon, 22 Apr 2019 05:07:00 +0000 (13:07 +0800)
build.gradle
src/main/java/com/supwisdom/dlpay/framework/filter/ValidateCodeFilter.java
src/main/java/com/supwisdom/dlpay/framework/security/ValidateCodeSecurityConfig.java
src/main/java/com/supwisdom/dlpay/framework/security/validate/VerifyCode.java
src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
src/main/kotlin/com/supwisdom/dlpay/framework/controller/security_controller.kt
src/main/kotlin/com/supwisdom/dlpay/security.kt
src/main/resources/application.properties
src/main/resources/templates/login.html

index d027592..6c9b719 100644 (file)
@@ -40,6 +40,7 @@ dependencies {
     implementation 'org.postgresql:postgresql:42.2.5'
     implementation 'com.jcabi:jcabi-manifests:1.1'
     implementation 'org.bitbucket.b_c:jose4j:0.6.3'
+    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
     implementation 'org.springframework.social:spring-social-web:1.1.6.RELEASE'
     implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
     implementation group: 'com.sun.jersey', name: 'jersey-client', version: '1.19'
index 3868edd..c7f5360 100755 (executable)
@@ -20,59 +20,59 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;\r
 \r
 \r
-@Component("validateCodeFilter")\r
-public class ValidateCodeFilter extends OncePerRequestFilter{\r
-\r
-       /**\r
-        * 校验失败处理器\r
-        */\r
-       @Autowired\r
-       private AuthenticationFailureHandler myAuthenticationFailureHandler;\r
-\r
-       /**\r
-        * 校验成功处理器\r
-        */\r
-       @Autowired\r
-       private AuthenticationSuccessHandler myAuthenticationSuccessHandler;\r
-\r
-\r
-       @Override\r
-       protected void doFilterInternal(HttpServletRequest request,\r
-                                                                                                                                       HttpServletResponse response, FilterChain filterChain)\r
-                       throws ServletException, IOException {\r
-               if (StringUtil.equals("/login/form", request.getRequestURI())\r
-                               && StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {\r
-                       try {\r
-                               validate(request);\r
-                       } catch (ValidateCodeException e) {\r
-                               myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);\r
-                       }\r
-               }\r
-               filterChain.doFilter(request, response);\r
-       }\r
-\r
-       private void validate(HttpServletRequest request) throws ValidateCodeException {\r
-               VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-               String inputCode;\r
-               try {\r
-                       inputCode = request.getParameter("imageCode");\r
-               } catch (Exception e) {\r
-                       throw new ValidateCodeException("获取验证码的值失败");\r
-               }\r
-               if (StringUtil.isEmpty(inputCode)) {\r
-                       throw new ValidateCodeException("验证码的值不能为空");\r
-               }\r
-               if (null == imageCode) {\r
-                       throw new ValidateCodeException("验证码不存在");\r
-               }\r
-               if (imageCode.isExpired()) {\r
-                       request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-                       throw new ValidateCodeException("验证码已过期");\r
-               }\r
-               if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {\r
-                       throw new ValidateCodeException("验证码不匹配");\r
-               }\r
-               request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
-       }\r
-\r
-}\r
+//@Component("validateCodeFilter")\r
+//public class ValidateCodeFilter extends OncePerRequestFilter{\r
+//\r
+//     /**\r
+//      * 校验失败处理器\r
+//      */\r
+//     @Autowired\r
+//     private AuthenticationFailureHandler myAuthenticationFailureHandler;\r
+//\r
+//     /**\r
+//      * 校验成功处理器\r
+//      */\r
+//     @Autowired\r
+//     private AuthenticationSuccessHandler myAuthenticationSuccessHandler;\r
+//\r
+//\r
+//     @Override\r
+//     protected void doFilterInternal(HttpServletRequest request,\r
+//                                                                                                                                     HttpServletResponse response, FilterChain filterChain)\r
+//                     throws ServletException, IOException {\r
+//             if (StringUtil.equals("/login/form", request.getRequestURI())\r
+//                             && StringUtil.equalsIgnoreCase(request.getMethod(), "post")) {\r
+//                     try {\r
+//                             validate(request);\r
+//                     } catch (ValidateCodeException e) {\r
+//                             myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);\r
+//                     }\r
+//             }\r
+//             filterChain.doFilter(request, response);\r
+//     }\r
+//\r
+//     private void validate(HttpServletRequest request) throws ValidateCodeException {\r
+//             VerifyCode imageCode = (VerifyCode) request.getSession().getAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+//             String inputCode;\r
+//             try {\r
+//                     inputCode = request.getParameter("imageCode");\r
+//             } catch (Exception e) {\r
+//                     throw new ValidateCodeException("获取验证码的值失败");\r
+//             }\r
+//             if (StringUtil.isEmpty(inputCode)) {\r
+//                     throw new ValidateCodeException("验证码的值不能为空");\r
+//             }\r
+//             if (null == imageCode) {\r
+//                     throw new ValidateCodeException("验证码不存在");\r
+//             }\r
+//             if (imageCode.isExpired()) {\r
+//                     request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+//                     throw new ValidateCodeException("验证码已过期");\r
+//             }\r
+//             if (!StringUtil.equalsIgnoreCase(imageCode.getText(), inputCode)) {\r
+//                     throw new ValidateCodeException("验证码不匹配");\r
+//             }\r
+//             request.getSession().removeAttribute(ImageCodeUtil.LOGIN_IMAGECODE_SESSIONKEY);\r
+//     }\r
+//\r
+//}\r
index 56782db..c5c3f7c 100644 (file)
@@ -9,7 +9,7 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
 import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
 import org.springframework.stereotype.Component;
 
-@Component("validateCodeSecurityConfig")
+//@Component("validateCodeSecurityConfig")
 public class ValidateCodeSecurityConfig  extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,HttpSecurity> {
        
        @Autowired
index 26d6ea5..74cc240 100644 (file)
@@ -5,10 +5,11 @@ import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.time.LocalDateTime;
 import java.util.Random;
 
-public class VerifyCode {
+public class VerifyCode implements Serializable {
   private int w = 100;
   private int h = 35;
   private Random r = new Random();
@@ -120,4 +121,6 @@ public class VerifyCode {
   public boolean isExpired(){
     return LocalDateTime.now().isAfter(localDateTime);
   }
+
+
 }
index 7ff33c6..580ee13 100644 (file)
@@ -1,10 +1,6 @@
 package com.supwisdom.dlpay
 
-import com.supwisdom.dlpay.framework.filter.ValidateCodeFilter
-import com.supwisdom.dlpay.framework.security.MyAuthenticationFailureHandler
-import com.supwisdom.dlpay.framework.security.MyAuthenticationSuccessHandler
 import io.lettuce.core.ReadFrom
-import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.autoconfigure.SpringBootApplication
 import org.springframework.boot.runApplication
@@ -15,7 +11,10 @@ import org.springframework.data.redis.connection.RedisPassword
 import org.springframework.data.redis.connection.RedisStandaloneConfiguration
 import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
 import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
+import org.springframework.data.redis.core.RedisTemplate
 import org.springframework.data.redis.repository.configuration.EnableRedisRepositories
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
+import org.springframework.data.redis.serializer.StringRedisSerializer
 
 
 @Configuration
@@ -49,6 +48,22 @@ class AppConfig {
     }
 }
 
+@Configuration
+class HttpSessionConfig {
+    @Bean
+    fun sessionRedisTemplate(
+            connectionFactory: RedisConnectionFactory): RedisTemplate<Any, Any> {
+        val template = RedisTemplate<Any, Any>()
+        template.keySerializer = StringRedisSerializer()
+        template.hashKeySerializer = StringRedisSerializer()
+
+        template.setDefaultSerializer(GenericJackson2JsonRedisSerializer())
+        template.setConnectionFactory(connectionFactory)
+        return template
+    }
+}
+
+
 @SpringBootApplication
 class PayApiApplication
 
index cd7d60f..3e13e8b 100644 (file)
@@ -15,9 +15,7 @@ import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.social.connect.web.HttpSessionSessionStrategy
 import org.springframework.stereotype.Controller
-import org.springframework.web.bind.annotation.GetMapping
-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.util.*
 import javax.imageio.ImageIO
@@ -103,7 +101,7 @@ class ApiAuthController {
 }
 
 @RestController
-class ValidateCodeController{
+class ValidateCodeController {
 
     @GetMapping("/code/image")
     fun createCode(request: HttpServletRequest, response: HttpServletResponse) {
@@ -113,6 +111,9 @@ class ValidateCodeController{
     }
 }
 
+
+data class LoginForm(val username: String, val password: String, val imageCode: String)
+
 @Controller
 class WebHomeController {
 
@@ -121,4 +122,10 @@ class WebHomeController {
 
     @GetMapping("/login")
     fun loginView() = "login"
+
+    @PostMapping("/login/form")
+    fun loginForm(form: LoginForm): String {
+        println(form.username)
+        return "index"
+    }
 }
index b529714..29c795d 100644 (file)
@@ -2,10 +2,6 @@ package com.supwisdom.dlpay
 
 import com.supwisdom.dlpay.framework.core.JwtConfig
 import com.supwisdom.dlpay.framework.core.JwtTokenUtil
-import com.supwisdom.dlpay.framework.filter.ValidateCodeFilter
-import com.supwisdom.dlpay.framework.security.MyInvalidSessionStrategy
-import com.supwisdom.dlpay.framework.security.ValidateCodeSecurityConfig
-import com.supwisdom.dlpay.framework.service.OperatorDetailService
 import org.jose4j.jwt.consumer.InvalidJwtException
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.context.annotation.Bean
@@ -19,21 +15,14 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.config.http.SessionCreationPolicy
 import org.springframework.security.core.authority.SimpleGrantedAuthority
 import org.springframework.security.core.context.SecurityContextHolder
-import org.springframework.security.core.session.SessionRegistry
-import org.springframework.security.core.session.SessionRegistryImpl
-import org.springframework.security.core.userdetails.User
-import org.springframework.security.core.userdetails.UserDetailsService
-import org.springframework.security.provisioning.InMemoryUserDetailsManager
-import org.springframework.security.web.authentication.AuthenticationFailureHandler
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
-import org.springframework.security.web.session.InvalidSessionStrategy
-import org.springframework.security.web.session.SessionInformationExpiredStrategy
+import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher
 import org.springframework.web.filter.OncePerRequestFilter
 import javax.servlet.FilterChain
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
+import javax.sql.DataSource
 
 
 class ApiJwtAuthenticationFilter(jwt: JwtTokenUtil) : OncePerRequestFilter() {
@@ -79,10 +68,10 @@ class WebSecurityConfig {
             @Autowired
             lateinit var jwtConfig: JwtConfig
 
+
             override fun configure(http: HttpSecurity) {
                 // 设置 API 访问权限管理
-                http.csrf().disable()
-                        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                         .and()
                         .addFilterAfter(ApiJwtAuthenticationFilter(JwtTokenUtil(jwtConfig)),
                                 UsernamePasswordAuthenticationFilter::class.java)
@@ -91,50 +80,58 @@ class WebSecurityConfig {
                         .antMatchers("/api/common/**").hasAnyRole("THIRD_COMMON", "THIRD_ADMIN")
                         .antMatchers("/api/consume/**").hasRole("THIRD_CONSUME")
                         .antMatchers("/api/deposit/**").hasRole("THIRD_DEPOSIT")
-                        .antMatchers("/api/user/**").hasAnyRole("THIRD_COMMON", "THIRD_ADMIN")
+                        .antMatchers("/api/user/**").hasRole("THIRD_ADMIN")
                         .antMatchers("/api/shop/**").hasRole("THIRD_SHOP")
-                // 注册 filter
+                        .and()
+                        .csrf().ignoringAntMatchers("/api/**")
             }
         }
 
         @Configuration
         class MvcWebSecurityConfigurationAdapter : WebSecurityConfigurerAdapter() {
             @Autowired
-            lateinit var validateCodeSecurityConfig: ValidateCodeSecurityConfig
-            @Autowired
-            lateinit var userDetailsService: OperatorDetailService
-            @Autowired
-            lateinit var myAuthenticationFailureHandler: AuthenticationFailureHandler
-            @Autowired
-            lateinit var myAuthenticationSuccessHandler: AuthenticationSuccessHandler
-            @Autowired
-            lateinit var myInvalidSessionStrategy: InvalidSessionStrategy
+            lateinit var dataSource: DataSource
+//            @Autowired
+//            lateinit var validateCodeSecurityConfig: ValidateCodeSecurityConfig
+//            @Autowired
+//            lateinit var userDetailsService: OperatorDetailService
+//            @Autowired
+//            lateinit var myAuthenticationFailureHandler: AuthenticationFailureHandler
+//            @Autowired
+//            lateinit var myAuthenticationSuccessHandler: AuthenticationSuccessHandler
+//            @Autowired
+//            lateinit var myInvalidSessionStrategy: InvalidSessionStrategy
 
 
+            @Bean
+            fun jdbcTokenImplement(): JdbcTokenRepositoryImpl {
+                return JdbcTokenRepositoryImpl().also {
+                    it.setDataSource(dataSource)
+                }
+            }
+
             override fun configure(http: HttpSecurity) {
                 // 设置 Web MVC 应用权限
-                http.apply(validateCodeSecurityConfig)
+                http.csrf()
+                        .and()
+                        .authorizeRequests()
+                        .antMatchers("/login", "/login/form").permitAll()
+                        .antMatchers("/static/**").permitAll()
+                        .antMatchers("/code/image").permitAll()
+                        .anyRequest().authenticated()
+                        .and()
+                        .sessionManagement()
+                        .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                         .and()
                         .formLogin()
                         .loginPage("/login")
                         .loginProcessingUrl("/login/form")
-                        .successHandler(myAuthenticationSuccessHandler)
-                        .failureHandler(myAuthenticationFailureHandler)
                         .and()
                         .logout()
                         .logoutRequestMatcher(AntPathRequestMatcher("/logout"))
                         .logoutSuccessUrl("/login")
                         .deleteCookies("JSESSIONID")
                         .invalidateHttpSession(true)
-                        .and()
-                        .userDetailsService(userDetailsService)
-                        .authorizeRequests()
-                        .antMatchers("/login").permitAll()
-                        .antMatchers("/static/**").permitAll()
-                        .antMatchers("/code/image").permitAll()
-                        .anyRequest().authenticated()
-                        .and()
-                        .formLogin()
                 // 设置 Web MVC 应用权限
 //                http.apply(validateCodeSecurityConfig)
 //                        .and()
index 0a24848..6abc8d8 100644 (file)
@@ -1,10 +1,5 @@
 #######################################springboot配置 start#################################
 # 单库数据库配置
-spring.datasource.url=jdbc:oracle:thin:@172.28.201.101:47922:orcl
-spring.datasource.username=dlpay
-spring.datasource.password=kingstar
-spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
-spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
 spring.jpa.show-sql=true
 spring.datasource.hikari.connection-timeout=60000
 spring.datasource.hikari.maximum-pool-size=5
@@ -23,14 +18,3 @@ spring.thymeleaf.encoding=UTF-8
 spring.thymeleaf.mode=HTML5
 spring.thymeleaf.cache=false
 spring.thymeleaf.enabled=true
-#################### Redis ####################
-# Redis settings
-redis.server=172.28.201.101
-redis.port=16379
-redis.password=kingstar
-redis.database=0
-#################### jwt ####################
-# jwt settings
-jwt.secret=Zj5taLomEbrM0lk+NMQZbHfSxaDU1wekjT+kiC3YzDw=
-jwt.expiration=3600
-jwt.header=payapi
\ No newline at end of file
index 4feb52a..919d862 100644 (file)
@@ -1,12 +1,15 @@
 <!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/payapi/css/login.css}"  />
 </head>
-
 <body>
 <div class="login-wrapper">
 
@@ -25,6 +28,9 @@
                     <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">