From 251576aa8a13b5d3eee7ab11247995d6e687f0e3 Mon Sep 17 00:00:00 2001 From: Tang Cheng Date: Mon, 22 Apr 2019 15:56:13 +0800 Subject: [PATCH] =?utf8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=93=8D=E4=BD=9C?= =?utf8?q?=E5=91=98=E7=99=BB=E9=99=86=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- sql/init_test.sql | 11 +++ .../dlpay/framework/domain/TOperator.java | 2 +- .../controller/security_controller.kt | 19 +++- .../kotlin/com/supwisdom/dlpay/security.kt | 86 ++++++++++++++----- .../com/supwisdom/dlpay/password_test.kt | 27 ++++++ 5 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 src/test/kotlin/com/supwisdom/dlpay/password_test.kt diff --git a/sql/init_test.sql b/sql/init_test.sql index c13e4cfe..3a210ae4 100644 --- a/sql/init_test.sql +++ b/sql/init_test.sql @@ -1,4 +1,15 @@ insert into tb_apiclient(appid, secret, status, roles) values ('100001', 'oUw2NmA09ficiVWD4TUQLDOkPyzQa3VzbjjsW0B2qTk=', 'normal', 'ROLE_THIRD_ADMIN'); +INSERT INTO tb_operator( + operid, closedate, opendate, opercode, opername, operpwd, opertype, status) + VALUES ('LOR2IwRkbOjp+sVG9KR2BpHZbwGKepS4', '20500101', '20190101', 'system', '系统管理员', '$2a$10$Ex9xp11.vCaD8D0a7ahiUOKqDij1TcCUBwRAmrqXeDvAkmzLibn4.', '', 'normal'); + +INSERT INTO tb_role( + role_id, createtime, editflag, lastsaved, role_code, role_desc, role_name) + VALUES ('d1yctWs5+ks0iQN3m9bUvRHus6HbKbrs', '20190101000000', 0, '', 'ROLE_ADMIN', '超级管理员', '超级管理员'); + +INSERT INTO tb_oper_role( + id, operid, role_id) + VALUES ('1', 'LOR2IwRkbOjp+sVG9KR2BpHZbwGKepS4', 'd1yctWs5+ks0iQN3m9bUvRHus6HbKbrs'); commit; \ No newline at end of file diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java b/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java index 1da944b9..83885d37 100644 --- a/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java +++ b/src/main/java/com/supwisdom/dlpay/framework/domain/TOperator.java @@ -26,7 +26,7 @@ public class TOperator implements UserDetails { @Column(name="OPERNAME", nullable = false, length = 100) private String opername; - @Column(name="OPERPWD", nullable = false, length = 32) + @Column(name="OPERPWD", nullable = false, length = 80) private String operpwd; @Column(name="STATUS", nullable = false, length = 32) 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 3e13e8b1..e1c06aab 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 @@ -13,10 +13,13 @@ import com.supwisdom.dlpay.framework.util.HmacUtil import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler import org.springframework.social.connect.web.HttpSessionSessionStrategy import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.* import org.springframework.web.context.request.ServletWebRequest +import sun.plugin.liveconnect.SecurityContextHelper import java.util.* import javax.imageio.ImageIO import javax.servlet.http.HttpServletRequest @@ -117,15 +120,23 @@ data class LoginForm(val username: String, val password: String, val imageCode: @Controller class WebHomeController { - @GetMapping("/{index}") + @GetMapping("/index") fun homeView() = "index" @GetMapping("/login") fun loginView() = "login" @PostMapping("/login/form") - fun loginForm(form: LoginForm): String { - println(form.username) - return "index" + fun loginForm(form: LoginForm): ResponseEntity { + return ResponseEntity.ok(ResponseBodyBuilder.create() + .success()) + } + + @GetMapping("/logout") + fun logout(request: HttpServletRequest, response: HttpServletResponse): String { + SecurityContextHolder.getContext().authentication?.also { + SecurityContextLogoutHandler().logout(request, response, it) + } + return "redirect:/login?logout" } } diff --git a/src/main/kotlin/com/supwisdom/dlpay/security.kt b/src/main/kotlin/com/supwisdom/dlpay/security.kt index 6fd99abc..7998f237 100644 --- a/src/main/kotlin/com/supwisdom/dlpay/security.kt +++ b/src/main/kotlin/com/supwisdom/dlpay/security.kt @@ -2,23 +2,38 @@ package com.supwisdom.dlpay import com.supwisdom.dlpay.framework.core.JwtConfig import com.supwisdom.dlpay.framework.core.JwtTokenUtil +import com.supwisdom.dlpay.framework.dao.OperRoleDao +import com.supwisdom.dlpay.framework.dao.OperatorDao import com.supwisdom.dlpay.framework.security.ValidateCodeSecurityConfig +import com.supwisdom.dlpay.framework.util.StringUtil import org.jose4j.jwt.consumer.InvalidJwtException import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.annotation.Order import org.springframework.http.HttpStatus +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException +import org.springframework.security.authentication.AuthenticationProvider import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider +import org.springframework.security.authentication.jaas.AuthorityGranter import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.core.Authentication +import org.springframework.security.core.AuthenticationException +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.AuthorityUtils 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.web.authentication.AuthenticationFailureHandler +import org.springframework.security.web.authentication.AuthenticationSuccessHandler import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl import org.springframework.security.web.util.matcher.AntPathRequestMatcher +import org.springframework.stereotype.Component import org.springframework.web.filter.OncePerRequestFilter import javax.servlet.FilterChain import javax.servlet.http.HttpServletRequest @@ -45,21 +60,50 @@ class ApiJwtAuthenticationFilter(jwt: JwtTokenUtil) : OncePerRequestFilter() { } } +@Component +class OperatorAuthenticationProvider : AuthenticationProvider { + @Autowired + lateinit var operatorDao: OperatorDao + + @Autowired + lateinit var operRoleDao: OperRoleDao + + override fun authenticate(authentication: Authentication?): Authentication { + return authentication?.let { + val oper = operatorDao.findByOpercode(it.name) + ?: throw AuthenticationCredentialsNotFoundException("操作员不存在或密码错误") + if (BCryptPasswordEncoder().matches(it.credentials.toString(), oper.password)) { + val roles = operRoleDao.getRolecodeByOperid(oper.operid) + if (roles.size > 0) { + oper.authorities = roles.mapNotNull { role -> + role?.let { + SimpleGrantedAuthority(role) + } + } + } else { + oper.authorities = listOf() + } + return UsernamePasswordAuthenticationToken(authentication.name, authentication.credentials, + oper.authorities) + } else { + throw AuthenticationCredentialsNotFoundException("操作员不存在或密码错误") + } + } ?: throw AuthenticationCredentialsNotFoundException("操作员不存在或密码错误") + } + + override fun supports(authentication: Class<*>?): Boolean { + return authentication?.equals(UsernamePasswordAuthenticationToken::class.java) + ?: false + } +} + @EnableWebSecurity class WebSecurityConfig { -// @Bean -// fun daoProvider(detailsService: UserDetailsService): DaoAuthenticationProvider { -// return DaoAuthenticationProvider().also { -// it.setUserDetailsService(detailsService) -// } -// } -// -// @Bean -// fun providerManager(daoProvider: DaoAuthenticationProvider): ProviderManager { -// return ProviderManager(listOf(daoProvider)) -// } - + @Bean + fun passwordEncoder(): BCryptPasswordEncoder { + return BCryptPasswordEncoder() + } companion object { @Configuration @@ -69,13 +113,13 @@ class WebSecurityConfig { @Autowired lateinit var jwtConfig: JwtConfig - override fun configure(http: HttpSecurity) { // 设置 API 访问权限管理 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterAfter(ApiJwtAuthenticationFilter(JwtTokenUtil(jwtConfig)), UsernamePasswordAuthenticationFilter::class.java) + .antMatcher("/api/**") .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .antMatchers("/api/notify/**").permitAll() @@ -84,6 +128,7 @@ class WebSecurityConfig { .antMatchers("/api/deposit/**").hasRole("THIRD_DEPOSIT") .antMatchers("/api/user/**").hasRole("THIRD_ADMIN") .antMatchers("/api/shop/**").hasRole("THIRD_SHOP") + .anyRequest().hasRole("THIRD_COMMON") .and() .csrf().ignoringAntMatchers("/api/**") } @@ -95,14 +140,10 @@ class WebSecurityConfig { 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 + @Autowired + lateinit var authenticationFailureHandler: AuthenticationFailureHandler + @Autowired + lateinit var authenticationSuccessHandler: AuthenticationSuccessHandler @Bean @@ -121,6 +162,7 @@ class WebSecurityConfig { .antMatchers("/login", "/login/form").permitAll() .antMatchers("/static/**").permitAll() .antMatchers("/code/image").permitAll() + .antMatchers("/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() .and() .sessionManagement() @@ -129,6 +171,8 @@ class WebSecurityConfig { .formLogin() .loginPage("/login") .loginProcessingUrl("/login/form") + .successHandler(authenticationSuccessHandler) + .failureHandler(authenticationFailureHandler) .and() .logout() .logoutRequestMatcher(AntPathRequestMatcher("/logout")) diff --git a/src/test/kotlin/com/supwisdom/dlpay/password_test.kt b/src/test/kotlin/com/supwisdom/dlpay/password_test.kt new file mode 100644 index 00000000..a89d7593 --- /dev/null +++ b/src/test/kotlin/com/supwisdom/dlpay/password_test.kt @@ -0,0 +1,27 @@ +package com.supwisdom.dlpay + +import org.junit.Test +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.DelegatingPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder +import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder +import java.util.* + + +class PasswordTest { + private fun encodePassword(pswd: String) { + val encoders = HashMap() + encoders["bcrypt"] = BCryptPasswordEncoder() + encoders["pbkdf2"] = Pbkdf2PasswordEncoder() + encoders["scrypt"] = SCryptPasswordEncoder() + val passwordEncoder = DelegatingPasswordEncoder("bcrypt", encoders) + println(passwordEncoder.encode(pswd)) + + } + + @Test + fun testPassword() { + encodePassword("123456") + } +} \ No newline at end of file -- 2.17.1