Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
loginservice
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lanmw
loginservice
Commits
197e182c
Commit
197e182c
authored
Nov 29, 2024
by
xieshaohua
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
前海电力项目sso初始化
parent
431a5802
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
803 additions
and
1 deletion
+803
-1
pom.xml
pom.xml
+1
-1
LoginApplication.java
src/main/java/com/keymobile/login/LoginApplication.java
+2
-0
ADApi.java
src/main/java/com/keymobile/login/api/ADApi.java
+222
-0
FeignClientConfig.java
...main/java/com/keymobile/login/conf/FeignClientConfig.java
+44
-0
Swagger2Config.java
src/main/java/com/keymobile/login/conf/Swagger2Config.java
+37
-0
LdapException.java
...ain/java/com/keymobile/login/exception/LdapException.java
+21
-0
LdapInfoRepository.java
...a/com/keymobile/login/persistence/LdapInfoRepository.java
+11
-0
LdapInfo.java
.../java/com/keymobile/login/persistence/model/LdapInfo.java
+81
-0
ADService.java
src/main/java/com/keymobile/login/service/ADService.java
+27
-0
AuthRemoteService.java
...n/java/com/keymobile/login/service/AuthRemoteService.java
+21
-0
ADServiceImpl.java
.../java/com/keymobile/login/service/impl/ADServiceImpl.java
+246
-0
AES.java
src/main/java/com/keymobile/login/util/AES.java
+90
-0
No files found.
pom.xml
View file @
197e182c
...
@@ -106,7 +106,7 @@
...
@@ -106,7 +106,7 @@
<dependency>
<dependency>
<groupId>
mysql
</groupId>
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
5.1.6
</version>
<version>
8.0.28
</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<groupId>
org.springframework.cloud
</groupId>
...
...
src/main/java/com/keymobile/login/LoginApplication.java
View file @
197e182c
...
@@ -3,11 +3,13 @@ package com.keymobile.login;
...
@@ -3,11 +3,13 @@ package com.keymobile.login;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.cloud.client.discovery.EnableDiscoveryClient
;
import
org.springframework.cloud.client.discovery.EnableDiscoveryClient
;
import
org.springframework.cloud.openfeign.EnableFeignClients
;
import
org.springframework.context.annotation.ComponentScan
;
import
org.springframework.context.annotation.ComponentScan
;
@SpringBootApplication
@SpringBootApplication
@EnableDiscoveryClient
@EnableDiscoveryClient
@ComponentScan
(
basePackages
=
{
"com.keymobile.login"
,
"com.keymobile.config.logging"
})
@ComponentScan
(
basePackages
=
{
"com.keymobile.login"
,
"com.keymobile.config.logging"
})
@EnableFeignClients
public
class
LoginApplication
{
public
class
LoginApplication
{
public
static
void
main
(
String
[]
args
)
{
public
static
void
main
(
String
[]
args
)
{
...
...
src/main/java/com/keymobile/login/api/ADApi.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
api
;
import
com.keymobile.login.persistence.model.LdapInfo
;
import
com.keymobile.login.service.ADService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
javax.naming.AuthenticationException
;
import
javax.naming.Context
;
import
javax.naming.NamingEnumeration
;
import
javax.naming.directory.*
;
import
javax.naming.ldap.InitialLdapContext
;
import
javax.naming.ldap.LdapContext
;
import
javax.servlet.http.HttpServletRequest
;
import
java.util.*
;
@Api
(
value
=
"AD域相关"
,
tags
=
"AD域相关"
)
@RestController
@RequestMapping
(
value
=
"/adApi"
)
public
class
ADApi
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ADApi
.
class
);
private
static
String
DEFAULT_TIME_OUT
=
"5000"
;
@Autowired
private
ADService
adService
;
@ApiOperation
(
value
=
"保存ldap基本信息"
)
@PostMapping
(
value
=
"/saveLdapInfo"
)
public
LdapInfo
saveLdapInfo
(
@RequestBody
LdapInfo
ldapInfo
)
{
return
adService
.
saveLdapInfo
(
ldapInfo
);
}
@ApiOperation
(
value
=
"获取ldap基本信息"
)
@GetMapping
(
value
=
"/getLdapInfo"
)
public
LdapInfo
getLdapInfo
()
{
return
adService
.
getLdapInfo
();
}
@ApiOperation
(
value
=
"删除ldap基本信息"
)
@DeleteMapping
(
value
=
"/deleteLdapInfo"
)
public
void
deleteLdapInfo
()
{
adService
.
deleteLdapInfo
();
}
@ApiOperation
(
value
=
"ad域账号登录"
,
notes
=
"ad域账号登录"
)
@PostMapping
(
value
=
"/login"
)
public
String
login
(
HttpServletRequest
request
,
@RequestParam
(
value
=
"username"
)
String
username
,
@RequestParam
(
value
=
"password"
)
String
password
)
{
return
adService
.
login
(
request
,
username
,
password
);
}
@ApiOperation
(
value
=
"测试ad账号连接"
,
notes
=
"测试ad账号连接"
)
@PostMapping
(
value
=
"/connect"
)
public
void
connect
(
@RequestParam
(
value
=
"host"
)
String
host
,
@RequestParam
(
value
=
"port"
)
String
port
,
@RequestParam
(
value
=
"username"
)
String
username
,
@RequestParam
(
value
=
"password"
)
String
password
)
{
logger
.
info
(
"connect start. host:{},port:{},username:{},password:{}"
,
host
,
port
,
username
,
password
);
try
{
LdapContext
ctx
=
null
;
Hashtable
<
String
,
String
>
hashEnv
=
new
Hashtable
<>();
//AD的用户名
hashEnv
.
put
(
Context
.
SECURITY_PRINCIPAL
,
username
);
//AD的密码
hashEnv
.
put
(
Context
.
SECURITY_CREDENTIALS
,
password
);
// LDAP工厂类
hashEnv
.
put
(
Context
.
INITIAL_CONTEXT_FACTORY
,
"com.sun.jndi.ldap.LdapCtxFactory"
);
hashEnv
.
put
(
"com.sun.jndi.ldap.connect.timeout"
,
DEFAULT_TIME_OUT
);
// 默认端口389
hashEnv
.
put
(
Context
.
PROVIDER_URL
,
"ldap://"
+
host
+
":"
+
port
);
try
{
// 初始化上下文
ctx
=
new
InitialLdapContext
(
hashEnv
,
null
);
logger
.
info
(
"{}身份验证成功!"
,
username
);
}
catch
(
AuthenticationException
e
)
{
logger
.
error
(
"身份验证失败!"
,
e
);
}
catch
(
javax
.
naming
.
CommunicationException
e
)
{
logger
.
error
(
"AD域连接失败!"
,
e
);
}
catch
(
Exception
e
)
{
logger
.
error
(
"身份验证未知异常!"
,
e
);
}
finally
{
if
(
null
!=
ctx
)
{
try
{
ctx
.
close
();
}
catch
(
Exception
e
)
{
logger
.
error
(
"上下文关闭错误!"
,
e
);
}
}
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"未知异常!"
,
e
);
}
}
@ApiOperation
(
value
=
"连接查询用户"
,
notes
=
"连接查询用户"
)
@PostMapping
(
value
=
"/connectAndSearchUser"
)
public
Object
connectAndSearchUser
(
@RequestParam
(
value
=
"host"
)
String
host
,
@RequestParam
(
value
=
"port"
)
String
port
,
@RequestParam
(
value
=
"username"
)
String
username
,
@RequestParam
(
value
=
"dn"
)
String
dn
,
@RequestParam
(
value
=
"password"
)
String
password
,
@RequestParam
(
value
=
"searchuser"
)
String
searchuser
)
{
logger
.
info
(
"connect start. host:{},port:{},username:{},password:{},dn:{}"
,
host
,
port
,
username
,
password
,
dn
);
List
<
Map
<
String
,
String
>>
users
=
new
ArrayList
<>();
try
{
String
initialContextFactory
=
"com.sun.jndi.ldap.LdapCtxFactory"
;
String
providerUrl
=
"ldap://"
+
host
+
":"
+
port
;
String
filterPrefix
=
"(&(objectCategory=Person)(sAMAccountName="
;
String
filterSuffix
=
"))"
;
Hashtable
<
String
,
String
>
env
=
new
Hashtable
<
String
,
String
>();
env
.
put
(
Context
.
INITIAL_CONTEXT_FACTORY
,
initialContextFactory
);
env
.
put
(
Context
.
PROVIDER_URL
,
providerUrl
);
env
.
put
(
Context
.
SECURITY_PRINCIPAL
,
username
);
env
.
put
(
Context
.
SECURITY_CREDENTIALS
,
password
);
LdapContext
context
=
new
InitialLdapContext
(
env
,
null
);
logger
.
info
(
"{}身份验证成功!"
,
username
);
//baseName 可以通过Softerra LDAP Browser查看
//AD-指定搜索范围为个人,并且用户名为客户端输入的用户名称
String
filter
=
filterPrefix
+
searchuser
+
filterSuffix
;
SearchControls
controls
=
new
SearchControls
();
controls
.
setSearchScope
(
SearchControls
.
SUBTREE_SCOPE
);
//AD-返回用户的名称、别名及部门
controls
.
setReturningAttributes
(
new
String
[]{
"sAMAccountName"
,
"cn"
,
"department"
});
NamingEnumeration
<
SearchResult
>
answer
=
context
.
search
(
dn
,
filter
,
controls
);
while
(
answer
.
hasMore
())
{
SearchResult
result
=
answer
.
next
();
Map
<
String
,
String
>
user
=
new
HashMap
<>();
Attributes
attrs
=
result
.
getAttributes
();
Attribute
attr
=
attrs
.
get
(
"sAMAccountName"
);
String
name
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
user
.
put
(
"sAMAccountName"
,
name
);
attr
=
attrs
.
get
(
"cn"
);
String
alias
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
user
.
put
(
"cn"
,
alias
);
attr
=
attrs
.
get
(
"department"
);
String
dept
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
user
.
put
(
"department"
,
dept
);
users
.
add
(
user
);
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"查询用户报错!"
,
e
);
}
return
users
;
}
@ApiOperation
(
value
=
"连接查询用户"
,
notes
=
"连接查询用户"
)
@PostMapping
(
value
=
"/searchAllUser"
)
public
void
searchAllUser
(
@RequestParam
(
value
=
"host"
)
String
host
,
@RequestParam
(
value
=
"port"
)
String
port
,
@RequestParam
(
value
=
"dn"
)
String
dn
,
@RequestParam
(
value
=
"username"
)
String
username
,
@RequestParam
(
value
=
"password"
)
String
password
)
{
logger
.
info
(
"searchAllUser start. host:{},port:{},username:{},password:{},dn:{}"
,
host
,
port
,
username
,
password
,
dn
);
try
{
Hashtable
<
String
,
String
>
hashEnv
=
new
Hashtable
<>();
String
LDAP_URL
=
"ldap://"
+
host
+
":"
+
port
;
hashEnv
.
put
(
Context
.
INITIAL_CONTEXT_FACTORY
,
"com.sun.jndi.ldap.LdapCtxFactory"
);
hashEnv
.
put
(
Context
.
PROVIDER_URL
,
LDAP_URL
);
hashEnv
.
put
(
Context
.
SECURITY_AUTHENTICATION
,
"simple"
);
hashEnv
.
put
(
Context
.
SECURITY_PRINCIPAL
,
username
);
hashEnv
.
put
(
Context
.
SECURITY_CREDENTIALS
,
password
);
logger
.
info
(
"env setting"
);
LdapContext
ctx
=
null
;
try
{
// 初始化上下文
ctx
=
new
InitialLdapContext
(
hashEnv
,
null
);
logger
.
info
(
"{}身份验证成功!"
,
username
);
SearchControls
searchControls
=
new
SearchControls
();
searchControls
.
setReturningAttributes
(
null
);
searchControls
.
setSearchScope
(
SearchControls
.
SUBTREE_SCOPE
);
String
userSearchFilter
=
"(&(objectCategory=person)(objectClass=user))"
;
NamingEnumeration
<
SearchResult
>
answer
=
ctx
.
search
(
dn
,
userSearchFilter
,
searchControls
);
List
<
Map
<
String
,
Object
>>
lm
=
new
ArrayList
<>();
while
(
answer
.
hasMore
())
{
SearchResult
result
=
answer
.
next
();
NamingEnumeration
<?
extends
Attribute
>
attrs
=
result
.
getAttributes
().
getAll
();
Map
<
String
,
Object
>
map
=
new
HashMap
<>();
while
(
attrs
.
hasMore
())
{
Attribute
attr
=
attrs
.
next
();
map
.
put
(
attr
.
getID
(),
attr
.
get
());
lm
.
add
(
map
);
}
}
logger
.
info
(
"获取到的用户属性为:"
+
lm
);
}
catch
(
AuthenticationException
e
)
{
logger
.
error
(
"身份验证失败!"
,
e
);
}
catch
(
javax
.
naming
.
CommunicationException
e
)
{
logger
.
error
(
"AD域连接失败!"
,
e
);
}
catch
(
Exception
e
)
{
logger
.
error
(
"身份验证未知异常!"
,
e
);
}
finally
{
if
(
null
!=
ctx
)
{
try
{
ctx
.
close
();
}
catch
(
Exception
e
)
{
logger
.
error
(
"上下文关闭错误!"
,
e
);
}
}
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"未知异常!"
,
e
);
}
}
}
src/main/java/com/keymobile/login/conf/FeignClientConfig.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
conf
;
import
com.fasterxml.jackson.databind.DeserializationFeature
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
feign.auth.BasicAuthRequestInterceptor
;
import
feign.codec.Decoder
;
import
org.springframework.beans.factory.ObjectFactory
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.autoconfigure.http.HttpMessageConverters
;
import
org.springframework.cloud.openfeign.support.ResponseEntityDecoder
;
import
org.springframework.cloud.openfeign.support.SpringDecoder
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.http.converter.HttpMessageConverter
;
import
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
;
@Configuration
public
class
FeignClientConfig
{
@Value
(
"${security.authUser}"
)
private
String
authUser
;
@Value
(
"${security.authPwd}"
)
private
String
authPwd
;
@Bean
public
BasicAuthRequestInterceptor
getBasicAuthRequestInterceptor
()
{
return
new
BasicAuthRequestInterceptor
(
authUser
,
authPwd
);
}
@Bean
public
Decoder
feignDecoder
()
{
HttpMessageConverter
jacksonConverter
=
new
MappingJackson2HttpMessageConverter
(
customObjectMapper
());
ObjectFactory
<
HttpMessageConverters
>
objectFactory
=
()
->
new
HttpMessageConverters
(
jacksonConverter
);
return
new
ResponseEntityDecoder
(
new
SpringDecoder
(
objectFactory
));
}
public
ObjectMapper
customObjectMapper
()
{
ObjectMapper
objectMapper
=
new
ObjectMapper
();
objectMapper
.
enable
(
DeserializationFeature
.
ACCEPT_SINGLE_VALUE_AS_ARRAY
);
return
objectMapper
;
}
}
\ No newline at end of file
src/main/java/com/keymobile/login/conf/Swagger2Config.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
conf
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
springfox.documentation.builders.ApiInfoBuilder
;
import
springfox.documentation.builders.PathSelectors
;
import
springfox.documentation.builders.RequestHandlerSelectors
;
import
springfox.documentation.service.ApiInfo
;
import
springfox.documentation.spi.DocumentationType
;
import
springfox.documentation.spring.web.plugins.Docket
;
import
springfox.documentation.swagger2.annotations.EnableSwagger2
;
@Configuration
@EnableSwagger2
public
class
Swagger2Config
{
@Bean
public
Docket
createRestApi
()
{
return
new
Docket
(
DocumentationType
.
SWAGGER_2
)
.
apiInfo
(
apiInfo
())
.
select
()
.
apis
(
RequestHandlerSelectors
.
basePackage
(
"com.keymobile.login.api"
))
.
paths
(
PathSelectors
.
any
())
.
build
();
}
private
ApiInfo
apiInfo
()
{
return
new
ApiInfoBuilder
()
.
title
(
"sso RESTful APIs"
)
.
description
(
"Spring Boot Swagger2"
)
.
termsOfServiceUrl
(
"http://www.keymobile.com.cn/"
)
// .contact("keymobile")
.
version
(
"1.0"
)
.
build
();
}
}
src/main/java/com/keymobile/login/exception/LdapException.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
exception
;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/11/21
* @desc
*/
public
class
LdapException
extends
Exception
{
private
static
final
long
serialVersionUID
=
1L
;
public
LdapException
(
String
errorMsg
)
{
super
(
errorMsg
);
}
public
LdapException
(
String
errorMsg
,
Throwable
cause
)
{
super
(
errorMsg
,
cause
);
}
}
src/main/java/com/keymobile/login/persistence/LdapInfoRepository.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
persistence
;
import
com.keymobile.login.persistence.model.LdapInfo
;
import
org.springframework.data.repository.CrudRepository
;
import
javax.transaction.Transactional
;
@Transactional
public
interface
LdapInfoRepository
extends
CrudRepository
<
LdapInfo
,
String
>
{
}
src/main/java/com/keymobile/login/persistence/model/LdapInfo.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
persistence
.
model
;
import
javax.persistence.Column
;
import
javax.persistence.Entity
;
import
javax.persistence.Id
;
import
javax.persistence.Table
;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/4/26
* @desc
*/
@Entity
@Table
(
name
=
"sso_ldap_info"
)
public
class
LdapInfo
{
@Id
private
String
id
;
@Column
(
name
=
"HOST"
,
nullable
=
false
)
private
String
host
;
@Column
(
name
=
"PORT"
,
nullable
=
false
)
private
String
port
;
@Column
(
name
=
"USER_NAME"
,
nullable
=
false
)
private
String
username
;
@Column
(
name
=
"PASSWORD"
,
nullable
=
false
)
private
String
password
;
@Column
(
name
=
"DN"
,
nullable
=
false
)
private
String
dn
;
public
String
getId
()
{
return
id
;
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
public
String
getHost
()
{
return
host
;
}
public
void
setHost
(
String
host
)
{
this
.
host
=
host
;
}
public
String
getPort
()
{
return
port
;
}
public
void
setPort
(
String
port
)
{
this
.
port
=
port
;
}
public
String
getUsername
()
{
return
username
;
}
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
public
String
getPassword
()
{
return
password
;
}
public
void
setPassword
(
String
password
)
{
this
.
password
=
password
;
}
public
String
getDn
()
{
return
dn
;
}
public
void
setDn
(
String
dn
)
{
this
.
dn
=
dn
;
}
}
src/main/java/com/keymobile/login/service/ADService.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
service
;
import
com.keymobile.login.persistence.model.LdapInfo
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
javax.servlet.http.HttpServletRequest
;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/11/20
* @desc ad域服务
*/
public
interface
ADService
{
LdapInfo
saveLdapInfo
(
LdapInfo
ldapInfo
);
LdapInfo
getLdapInfo
();
void
deleteLdapInfo
();
String
ldapAuthentication
(
String
username
,
String
password
);
String
login
(
HttpServletRequest
request
,
String
username
,
String
password
)
;
}
src/main/java/com/keymobile/login/service/AuthRemoteService.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
service
;
import
org.springframework.cloud.openfeign.FeignClient
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.List
;
import
java.util.Map
;
@FeignClient
(
value
=
"authService"
)
public
interface
AuthRemoteService
{
@RequestMapping
(
value
=
"/users/findByName"
)
List
<
Map
<
String
,
Object
>>
getUserByName
(
@RequestParam
(
value
=
"match"
)
String
match
);
@PostMapping
(
value
=
"/users"
)
Map
<
String
,
Object
>
addUser
(
@RequestBody
Map
<
String
,
Object
>
user
);
@PostMapping
(
value
=
"/users/{userId}"
)
Map
<
String
,
Object
>
updateUser
(
@PathVariable
(
value
=
"userId"
)
Long
userId
,
@RequestBody
Map
<
String
,
Object
>
user
);
}
src/main/java/com/keymobile/login/service/impl/ADServiceImpl.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
service
.
impl
;
import
com.keymobile.auth.common.security.CustomizedUserDetailService
;
import
com.keymobile.crypto.aes.AESUtil
;
import
com.keymobile.login.api.ADApi
;
import
com.keymobile.login.api.Constants
;
import
com.keymobile.login.exception.LdapException
;
import
com.keymobile.login.logging.LogConstants
;
import
com.keymobile.login.logging.LogManager
;
import
com.keymobile.login.persistence.LdapInfoRepository
;
import
com.keymobile.login.persistence.model.LdapInfo
;
import
com.keymobile.login.service.ADService
;
import
com.keymobile.login.service.AuthRemoteService
;
import
com.keymobile.login.util.AES
;
import
org.apache.commons.lang3.StringUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.slf4j.MDC
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.security.core.userdetails.UserDetails
;
import
org.springframework.security.web.authentication.WebAuthenticationDetails
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.CollectionUtils
;
import
javax.naming.AuthenticationException
;
import
javax.naming.Context
;
import
javax.naming.NamingEnumeration
;
import
javax.naming.directory.*
;
import
javax.naming.ldap.InitialLdapContext
;
import
javax.naming.ldap.LdapContext
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpSession
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* @author xiesh
* @version 1.0.0
* @date 2024/11/20
* @desc ad域服务实现
*/
@Service
public
class
ADServiceImpl
implements
ADService
{
@Autowired
private
LdapInfoRepository
ldapInfoRepository
;
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ADApi
.
class
);
private
static
String
DEFAULT_TIME_OUT
=
"5000"
;
@Autowired
private
AuthRemoteService
authService
;
@Autowired
private
CustomizedUserDetailService
customizedUserDetailService
;
public
static
final
String
LDAP_ID
=
"ldap"
;
public
static
final
String
LADP_S_AMACCOUNT_NAME
=
"sAMAccountName"
;
public
static
final
String
LADP_DISTINGUISHED_NAME
=
"distinguishedName"
;
public
static
final
String
LADP_CN
=
"cn"
;
@Override
public
LdapInfo
saveLdapInfo
(
LdapInfo
ldapInfo
)
{
ldapInfo
.
setId
(
LDAP_ID
);
LdapInfo
oldLdap
=
getLdapInfo
();
if
(
oldLdap
==
null
||
!
StringUtils
.
equals
(
ldapInfo
.
getPassword
(),
oldLdap
.
getPassword
()))
{
ldapInfo
.
setPassword
(
AES
.
encrypt
(
ldapInfo
.
getPassword
()));
}
return
ldapInfoRepository
.
save
(
ldapInfo
);
}
@Override
public
LdapInfo
getLdapInfo
()
{
return
ldapInfoRepository
.
findById
(
LDAP_ID
).
orElse
(
null
);
}
@Override
public
void
deleteLdapInfo
()
{
ldapInfoRepository
.
deleteAll
();
}
@Override
public
String
ldapAuthentication
(
String
username
,
String
password
)
{
LdapInfo
ldapInfo
=
getLdapInfo
();
if
(
ldapInfo
==
null
)
{
return
"未配置ldap"
;
}
String
result
=
"ok"
;
LdapContext
ctx
=
null
;
Hashtable
<
String
,
String
>
HashEnv
=
new
Hashtable
<>();
String
dn
=
ldapInfo
.
getDn
();
//拼接ldap可识别的用户名 用户名@域名
String
ldapUserName
=
username
+
"@"
+
dn
;
String
url
=
"ldap://"
+
ldapInfo
.
getHost
()
+
":"
+
ldapInfo
.
getPort
();
// LDAP访问安全级别(none,simple,strong)
// HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
//AD的用户名
HashEnv
.
put
(
Context
.
SECURITY_PRINCIPAL
,
ldapUserName
);
//AD的密码
HashEnv
.
put
(
Context
.
SECURITY_CREDENTIALS
,
password
);
// LDAP工厂类
HashEnv
.
put
(
Context
.
INITIAL_CONTEXT_FACTORY
,
"com.sun.jndi.ldap.LdapCtxFactory"
);
HashEnv
.
put
(
"com.sun.jndi.ldap.connect.timeout"
,
DEFAULT_TIME_OUT
);
// 默认端口389
HashEnv
.
put
(
Context
.
PROVIDER_URL
,
url
);
logger
.
debug
(
"ad域账号校验,url:{},username:{},password:{},"
,
url
,
ldapUserName
,
password
);
try
{
// 初始化上下文
ctx
=
new
InitialLdapContext
(
HashEnv
,
null
);
logger
.
info
(
"{}身份验证成功!"
,
username
);
}
catch
(
Exception
e
)
{
logger
.
error
(
"身份验证异常!"
,
e
);
result
=
"身份验证异常"
;
}
finally
{
if
(
null
!=
ctx
)
{
try
{
ctx
.
close
();
}
catch
(
Exception
e
)
{
logger
.
error
(
"上下文关闭错误!"
,
e
);
}
}
}
return
result
;
}
@Override
public
String
login
(
HttpServletRequest
request
,
String
username
,
String
password
)
{
String
result
=
null
;
try
{
result
=
ldapAuthentication
(
username
,
password
);
if
(
StringUtils
.
equals
(
result
,
"ok"
))
{
Map
<
String
,
Object
>
user
=
getUserByName
(
username
);
if
(
null
==
user
||
CollectionUtils
.
isEmpty
(
user
))
{
Map
<
String
,
String
>
ldapUserInfo
=
searchUserInfoByName
(
username
);
Map
<
String
,
Object
>
toAdd
=
new
HashMap
<>();
toAdd
.
put
(
"name"
,
username
);
toAdd
.
put
(
"dname"
,
ldapUserInfo
.
get
(
LADP_CN
)
==
null
?
username
:
ldapUserInfo
.
get
(
LADP_CN
));
//不能被匹配的加密字符
toAdd
.
put
(
"password"
,
"37fa265330ad83eaa879efb12312db6380896cf639"
);
logger
.
info
(
"新增用户:{}"
,
toAdd
);
authService
.
addUser
(
toAdd
);
}
//设置用户session
UserDetails
userDetails
=
customizedUserDetailService
.
loadUserByUsername
(
username
);
UsernamePasswordAuthenticationToken
authentication
=
new
UsernamePasswordAuthenticationToken
(
userDetails
,
userDetails
.
getPassword
(),
userDetails
.
getAuthorities
());
authentication
.
setDetails
(
new
WebAuthenticationDetails
(
request
));
SecurityContextHolder
.
getContext
().
setAuthentication
(
authentication
);
HttpSession
session
=
request
.
getSession
(
true
);
session
.
setAttribute
(
"SPRING_SECURITY_CONTEXT"
,
SecurityContextHolder
.
getContext
());
logger
.
info
(
"单点登录用户:"
+
username
);
MDC
.
put
(
"user"
,
username
);
MDC
.
put
(
"session"
,
session
.
getId
());
LogManager
.
logInfo
(
LogConstants
.
CTX_API
,
"统一账号认证登录"
);
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"登录异常!"
,
e
);
result
=
e
.
getMessage
();
}
return
result
;
}
private
Map
<
String
,
String
>
searchUserInfoByName
(
String
searchName
)
throws
Exception
{
LdapInfo
ldapInfo
=
getLdapInfo
();
if
(
ldapInfo
==
null
)
{
throw
new
LdapException
(
"未配置ldap信息"
);
}
Map
<
String
,
String
>
userInfo
=
new
HashMap
<>();
Hashtable
<
String
,
String
>
HashEnv
=
new
Hashtable
<>();
String
ldapUrl
=
"ldap://"
+
ldapInfo
.
getHost
()
+
":"
+
ldapInfo
.
getPort
();
String
ldapUser
=
ldapInfo
.
getUsername
()
+
"@"
+
ldapInfo
.
getDn
();
String
password
=
AES
.
decrypt
(
ldapInfo
.
getPassword
());
HashEnv
.
put
(
Context
.
INITIAL_CONTEXT_FACTORY
,
"com.sun.jndi.ldap.LdapCtxFactory"
);
HashEnv
.
put
(
Context
.
PROVIDER_URL
,
ldapUrl
);
// HashEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
HashEnv
.
put
(
Context
.
SECURITY_PRINCIPAL
,
ldapUser
);
HashEnv
.
put
(
Context
.
SECURITY_CREDENTIALS
,
password
);
logger
.
info
(
"env setting"
);
LdapContext
ctx
=
null
;
String
[]
dnArg
=
StringUtils
.
split
(
ldapInfo
.
getDn
(),
"."
);
List
<
String
>
dnString
=
Arrays
.
asList
(
dnArg
).
stream
().
map
(
e
->
"DC="
+
e
).
collect
(
Collectors
.
toList
());
String
dnStr
=
StringUtils
.
join
(
dnString
,
","
);
try
{
logger
.
debug
(
"ad域账号校验,url:{},username:{},password:{},dnStr:{}"
,
ldapUrl
,
ldapUser
,
password
,
dnStr
);
// 初始化上下文
ctx
=
new
InitialLdapContext
(
HashEnv
,
null
);
logger
.
info
(
"{}身份验证成功!"
,
ldapInfo
.
getUsername
());
SearchControls
searchControls
=
new
SearchControls
();
searchControls
.
setSearchScope
(
SearchControls
.
SUBTREE_SCOPE
);
searchControls
.
setReturningAttributes
(
new
String
[]{
LADP_S_AMACCOUNT_NAME
,
LADP_CN
,
LADP_DISTINGUISHED_NAME
});
String
userSearchFilter
=
String
.
format
(
"(&(objectCategory=person)(objectClass=user)(sAMAccountName=%s))"
,
searchName
);
NamingEnumeration
<
SearchResult
>
answer
=
ctx
.
search
(
dnStr
,
userSearchFilter
,
searchControls
);
logger
.
debug
(
"查询,dnStr:{},userSearchFilter:{},searchName:{}"
,
dnStr
,
userSearchFilter
,
searchName
);
if
(
answer
.
hasMore
())
{
SearchResult
result
=
answer
.
next
();
Attributes
attrs
=
result
.
getAttributes
();
Attribute
attr
=
attrs
.
get
(
LADP_S_AMACCOUNT_NAME
);
String
name
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
userInfo
.
put
(
LADP_S_AMACCOUNT_NAME
,
name
);
attr
=
attrs
.
get
(
LADP_CN
);
String
alias
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
userInfo
.
put
(
LADP_CN
,
alias
);
attr
=
attrs
.
get
(
LADP_DISTINGUISHED_NAME
);
String
dept
=
attr
==
null
||
attr
.
get
()
==
null
?
null
:
attr
.
get
().
toString
();
userInfo
.
put
(
LADP_DISTINGUISHED_NAME
,
dept
);
}
else
{
logger
.
info
(
"查询不到用户:{}"
,
searchName
);
}
}
catch
(
Exception
e
)
{
throw
e
;
}
finally
{
if
(
null
!=
ctx
)
{
try
{
ctx
.
close
();
}
catch
(
Exception
e
)
{
logger
.
error
(
"上下文关闭错误!"
,
e
);
}
}
}
logger
.
debug
(
"查询结果,userInfo:{}"
,
userInfo
);
return
userInfo
;
}
private
Map
<
String
,
Object
>
getUserByName
(
String
username
)
{
List
<
Map
<
String
,
Object
>>
matchUser
=
authService
.
getUserByName
(
username
);
if
(
matchUser
!=
null
)
{
Map
<
String
,
Object
>
user
=
matchUser
.
stream
().
filter
(
e
->
StringUtils
.
equals
(
username
,
String
.
valueOf
(
e
.
get
(
"name"
))))
.
findFirst
().
orElse
(
null
);
return
user
;
}
return
null
;
}
}
src/main/java/com/keymobile/login/util/AES.java
0 → 100644
View file @
197e182c
package
com
.
keymobile
.
login
.
util
;
import
org.apache.commons.lang.StringUtils
;
import
sun.misc.BASE64Decoder
;
import
sun.misc.BASE64Encoder
;
import
javax.crypto.Cipher
;
import
javax.crypto.spec.SecretKeySpec
;
public
class
AES
{
//编码方式
public
static
final
String
CODE_TYPE
=
"UTF-8"
;
//填充类型
public
static
final
String
AES_TYPE
=
"AES/ECB/PKCS5Padding"
;
//私钥
private
static
String
AES_KEY
=
"4444111133332222"
;
//AES固定格式为128/192/256 bits.即:16/24/32bytes。DES固定格式为128bits,即8bytes。
/**
* 加密
*
* @param cleartext
* @return
*/
public
static
String
encrypt
(
String
cleartext
)
{
//加密方式: AES128(CBC/PKCS5Padding) + Base64, 私钥:1111222233334444
try
{
if
(
StringUtils
.
isNotBlank
(
cleartext
)){
//IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
//两个参数,第一个为私钥字节数组, 第二个为加密方式 AES或者DES
SecretKeySpec
key
=
new
SecretKeySpec
(
AES_KEY
.
getBytes
(),
"AES"
);
//实例化加密类,参数为加密方式,要写全
Cipher
cipher
=
Cipher
.
getInstance
(
AES_TYPE
);
//PKCS5Padding比PKCS7Padding效率高,PKCS7Padding可支持IOS加解密
//初始化,此方法可以采用三种方式,按加密算法要求来添加。(1)无第三个参数(2)第三个参数为SecureRandom random = new SecureRandom();中random对象,随机数。(AES不可采用这种方法)(3)采用此代码中的IVParameterSpec
//加密时使用:ENCRYPT_MODE; 解密时使用:DECRYPT_MODE;
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
key
);
//CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
//加密操作,返回加密后的字节数组,然后需要编码。主要编解码方式有Base64, HEX, UUE,7bit等等。此处看服务器需要什么编码方式
byte
[]
encryptedData
=
cipher
.
doFinal
(
cleartext
.
getBytes
(
CODE_TYPE
));
return
new
BASE64Encoder
().
encode
(
encryptedData
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
/**
* 解密
*
* @param encrypted
* @return
*/
public
static
String
decrypt
(
String
encrypted
)
{
try
{
if
(
StringUtils
.
isNotBlank
(
encrypted
)){
byte
[]
byteMi
=
new
BASE64Decoder
().
decodeBuffer
(
encrypted
);
//IvParameterSpec zeroIv = new IvParameterSpec(VIPARA.getBytes());
SecretKeySpec
key
=
new
SecretKeySpec
(
AES_KEY
.
getBytes
(),
"AES"
);
Cipher
cipher
=
Cipher
.
getInstance
(
AES_TYPE
);
//与加密时不同MODE:Cipher.DECRYPT_MODE
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
key
);
//CBC类型的可以在第三个参数传递偏移量zeroIv,ECB没有偏移量
byte
[]
decryptedData
=
cipher
.
doFinal
(
byteMi
);
return
new
String
(
decryptedData
,
CODE_TYPE
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
public
static
void
setAesKey
(
String
aesKey
){
AES_KEY
=
aesKey
;
}
/**
* 测试
*
* @param args
* @throws Exception
*/
public
static
void
main
(
String
[]
args
)
throws
Exception
{
String
pass
=
new
String
(
"Szse@pwd0528"
);
setAesKey
(
"4444111133332222"
);
System
.
out
.
println
(
"加密内容:"
+
encrypt
(
pass
));
String
content
=
new
String
(
encrypt
(
pass
));
System
.
out
.
println
(
"解密内容:"
+
decrypt
(
content
));
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment