1、前言
公司使用ldap进行登录验证的,springboot在整合springsecurity后,再想整合ldap进行登录验证,发现无从下手,网上的springsecurity整合ldap教程要么没法用,要么都是整合数据库进行登录的,后来捣鼓,终于实现了。
2、实现思路
springsecurity整合ldap的原理,与springsecurity整合数据库的原理类似,只是在进行登录验证的时候,不走数据库验证,也不走springsecurity的ldap验证,只使用ldap来控制登录的账号和密码的正确性。
首先,springsecurity的验证逻辑都需要存在的,比如
1. WebSecurityConfigurerAdapter
2. UserDetailsService
3. 登录接口
我们系统整合的是"若依"的权限管理系统,但是使用的是session,而不是token
3、基础部分的代码
3.1、pom.xml有关的代码
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-ldap</artifactId>
</dependency>
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2、LoginUser代码
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
/**
* 登录用户身份权限
*
* @author 睡竹
*/
public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private String deptId;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public String getDeptId()
{
return deptId;
}
public void setDeptId(String deptId)
{
this.deptId = deptId;
}
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, String deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword()
{
return user.getPassword();
}
@Override
public String getUsername()
{
return user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired()
{
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled()
{
return true;
}
public Long getLoginTime()
{
return loginTime;
}
public void setLoginTime(Long loginTime)
{
this.loginTime = loginTime;
}
public String getIpaddr()
{
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
this.ipaddr = ipaddr;
}
public String getLoginLocation()
{
return loginLocation;
}
public void setLoginLocation(String loginLocation)
{
this.loginLocation = loginLocation;
}
public String getBrowser()
{
return browser;
}
public void setBrowser(String browser)
{
this.browser = browser;
}
public String getOs()
{
return os;
}
public void setOs(String os)
{
this.os = os;
}
public Long getExpireTime()
{
return expireTime;
}
public void setExpireTime(Long expireTime)
{
this.expireTime = expireTime;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
public SysUser getUser()
{
return user;
}
public void setUser(SysUser user)
{
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
return null;
}
}
3.1、SysUser代码
import java.util.Date;
import java.util.List;
import javax.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 用户对象 sys_user
*
* @author 睡竹 源于若依
*/
public class SysUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 用户ID */
private Long userId;
/** 部门ID */
private String deptId;
/** 用户账号 */
private String userName;
/** 用户昵称 */
private String nickName;
/** 用户邮箱 */
private String email;
/** 手机号码 */
private String phonenumber;
/** 用户性别 */
private String sex;
/** 用户头像 */
private String avatar;
/** 密码 */
private String password;
/** 盐加密 */
private String salt;
/** 帐号状态(0正常 1停用) */
private String status;
/** 删除标志(0代表存在 2代表删除) */
private String delFlag;
/** 最后登录IP */
private String loginIp;
/** 最后登录时间 */
private Date loginDate;
/** 部门对象 */
private SysDept dept;
/** 角色对象 */
private List<SysRole> roles;
/** 角色组 */
private Long[] roleIds;
/** 岗位组 */
private Long[] postIds;
/** 角色ID */
private Long roleId;
public SysUser()
{
}
public SysUser(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public boolean isAdmin()
{
return isAdmin(this.userId);
}
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
public String getDeptId()
{
return deptId;
}
public void setDeptId(String deptId)
{
this.deptId = deptId;
}
public String getNickName()
{
return nickName;
}
public void setNickName(String nickName)
{
this.nickName = nickName;
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getPhonenumber()
{
return phonenumber;
}
public void setPhonenumber(String phonenumber)
{
this.phonenumber = phonenumber;
}
public String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getAvatar()
{
return avatar;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getSalt()
{
return salt;
}
public void setSalt(String salt)
{
this.salt = salt;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getDelFlag()
{
return delFlag;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getLoginIp()
{
return loginIp;
}
public void setLoginIp(String loginIp)
{
this.loginIp = loginIp;
}
public Date getLoginDate()
{
return loginDate;
}
public void setLoginDate(Date loginDate)
{
this.loginDate = loginDate;
}
public SysDept getDept()
{
return dept;
}
public void setDept(SysDept dept)
{
this.dept = dept;
}
public List<SysRole> getRoles()
{
return roles;
}
public void setRoles(List<SysRole> roles)
{
this.roles = roles;
}
public Long[] getRoleIds()
{
return roleIds;
}
public void setRoleIds(Long[] roleIds)
{
this.roleIds = roleIds;
}
public Long[] getPostIds()
{
return postIds;
}
public void setPostIds(Long[] postIds)
{
this.postIds = postIds;
}
public Long getRoleId()
{
return roleId;
}
public void setRoleId(Long roleId)
{
this.roleId = roleId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("userId", getUserId())
.append("deptId", getDeptId())
.append("userName", getUserName())
.append("nickName", getNickName())
.append("email", getEmail())
.append("phonenumber", getPhonenumber())
.append("sex", getSex())
.append("avatar", getAvatar())
.append("password", getPassword())
.append("salt", getSalt())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())
.append("loginDate", getLoginDate())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("dept", getDept())
.toString();
}
}
3.4、ldap配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldaps://192.168.118.35:636" />
<!-- <property name="base" value="DC=shuizhu,DC=com,DC=cn" /> -->
<property name="userDn" value="domain1\\shuizhu" />
<property name="password" value="shuizhu123456" />
<property name="baseEnvironmentProperties">
<map>
<entry key="java.naming.security.protocol" value="ssl" />
<entry key="java.naming.ldap.factory.socket" value="com.shuizhu.CustomSSLSocketFactory" />
</map>
</property>
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="ldapContextSource" />
</bean>
</beans>
3.5、自定义SSL类
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class CustomSSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public CustomSSLSocketFactory() {
super();
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[] { new DummyTrustmanager() }, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch (Exception e) {
throw new BaseException("create SSLSocketFactory failed", e);
}
}
public static SocketFactory getDefault() {
return new CustomSSLSocketFactory();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return socketFactory.createSocket(socket, host, port, autoClose);
}
@Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return socketFactory.createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
return socketFactory.createSocket(host, port, localHost, localPort);
}
/**
* 证书
*/
public static class DummyTrustmanager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
3.6、异常处理类
import java.text.MessageFormat;
/**
* 基础异常类
*
* @author 睡竹
*/
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String code;
public BaseException() {
super();
this.code = ExceptionEnum.ER9999.getCode();
}
public BaseException(String message) {
super(message);
this.code = ExceptionEnum.ER9999.getCode();
}
public BaseException(String code, String message) {
super(message);
this.code = code;
}
public BaseException(String message, Throwable e) {
super(message, e);
this.code = ExceptionEnum.ER9999.getCode();
}
public BaseException(String code, String message, Object... arguments) {
super(MessageFormat.format(message, arguments));
this.code = code;
}
public BaseException(String code, String message, Throwable e) {
super(message, e);
this.code = code;
}
public BaseException(String code, String message, Throwable e, Object... arguments) {
super(MessageFormat.format(message, arguments), e);
this.code = code;
}
public BaseException(ExceptionEnum exceptionEnum) {
this(exceptionEnum.getCode(), exceptionEnum.getMessage());
}
public BaseException(ExceptionEnum exceptionEnum, Object... arguments) {
this(exceptionEnum.getCode(), exceptionEnum.getMessage(), arguments);
}
public BaseException(ExceptionEnum exceptionEnum, Throwable e) {
this(exceptionEnum.getCode(), exceptionEnum.getMessage(), e);
}
public BaseException(ExceptionEnum exceptionEnum, Throwable e, Object... arguments) {
this(exceptionEnum.getCode(), exceptionEnum.getMessage(), e, arguments);
}
public String getCode() {
return code;
}
}
4、springsecurity+ldap认证代码
4.1、登录接口代码:
public class LoginController{
@Autowired
private SysLoginService loginService;
/**
* 登录
*
* @param reqMap
*
*/
@PostMapping(value = "/login.json", produces = { "application/json;charset=UTF-8" })
@ResponseBody
public ResponseVo<Object> pwdLogin(@RequestBody Map<String, String> reqMap) {
String userName = reqMap.get("userName");
String password = reqMap.get("password");
return loginService.login(userName, password);
}
}
3.2、登录service代码【部分import代码已删除】
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* 登录校验方法
*
* @author 睡竹
*/
@Component
public class SysLoginService {
@Resource
private AuthenticationManager authenticationManager;
@Autowired
private ISysUserService userService;
/**
* 登录验证
*
* @param userName 用户名
* @param password 密码
* @return 结果
*/
public ResponseVo<Object> login(String userName, String password) {
// 用户验证
Authentication authentication;
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, password);
authentication = authenticationManager.authenticate(authenticationToken);
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
return ResponseVo.success("登录成功");
}
}
3.3、UserDetailsServiceImpl代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.ldap.core.LdapTemplate;
import com.alibaba.fastjson.JSONObject;
/**
* 用户验证处理
*
* @author 睡竹
*/
@Service
public class UserDetailsServiceImpl extends BaseCtrl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private ISysUserService userService;
@Autowired
private LdapTemplate ldapTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从会话中获取密码
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (ObjectUtils.isEmpty(requestAttributes)) {
throw new PortalException("登录错误@无法从session中获取用户密码");
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String password = null;
try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null) {
responseStrBuilder.append(inputStr);
}
JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
password = jsonObject.getString("password");
// 用户验证 !!!!LDAP关键代码,如果域存在该用户,则登录成功
try {
String dn = StringUtils.substringBefore("domain\\shuizhu", "\\");
ldapTemplate.getContextSource().getContext(dn + "\\" + username, password);
} catch (Exception e) {
LOGGER.error("登录失败", e);
if (e.getMessage().contains("775")) {
//账号锁定
throw new PortalException("域账号已锁定");
} else if (e.getMessage().contains("52e")) {
throw new BaseException ("用户名或密码错误");
} else if (e.getMessage().contains("532")) {
throw new BaseException ("密码过期");
} else if (e.getMessage().contains("533")) {
throw new BaseException ("账户已禁用");
} else if (e.getMessage().contains("701")) {
throw new BaseException ("账户过期");
} else if (e.getMessage().contains("773")) {
throw new BaseException ("用户必须重置密码");
} else if (e.getMessage().contains("Connection reset")) {
throw new BaseException ("域异常!请稍后重试");
}
return throw new BaseException ("域认证失败:"+e.getMessage());
}
//域通过认证后,查询该用户是否存在于系统的数据库中,如果不存在,则在该系统不存在该用户的访问权限,userService.selectUserByUserName(username)就是简单的查询数据库,所以代码不放出来了
SysUser user = userService.selectUserByUserName(username);
if (ObjectUtils.isEmpty(user)) {
log.info("登录用户:{} 不存在.", username);
throw new ServiceException("登录错误@登录用户:" + username + " 无权访问!请联系管理员");
}
//查询到用户信息后,把用户信息存储至session中,该系统使用的是session,不是token
setSession("userName", username);
setSession("isLogin", Boolean.TRUE);
// 获取用户信息
// 获取部门
String deptName = user.getDept().getDeptName();
//获取中文名
String nickName = user.getNickName();
setSession("depName", deptName);
setSession("username",nickName );
LoginUser loginUser = new LoginUser(user.getUserId(), user);
SysUser user1 = loginUser.getUser();
user1.setPassword(password);
return loginUser;
}
return null;
}
}
3.4、spring security配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* spring security配置
*
* @author 睡竹
*/
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
httpSecurity
// CSRF禁用
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 需要session,这句代码可以省略
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许直接访问
.antMatchers("/**/login.json","/**/userinfo.json", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll().antMatchers(
"/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证 优先鉴定权限
.anyRequest().authenticated().and().headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/api/logout.json").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
/**
* 不适用
*/
@Bean
public NoOpPasswordEncoder noOpPasswordEncoder() {
return new NoOpPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(noOpPasswordEncoder());
}
}
3.5、认证失败处理类
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
/**
* 认证失败处理类 返回未授权
*
* @author 睡竹
*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = 1L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
// int code = HttpStatus.UNAUTHORIZED;
//String msg = StringUtils.format("请求访问:{},用户认证失败", request.getRequestURI());
String msg = StringUtils.format("会话过期!请重新登录");
ResponseVo<Object> vo = new ResponseVo<>();
vo.setRetCode("9001");
vo.setRetMsg(msg);
ServletUtils.renderString(response, JSON.toJSONString(vo));
}
}
3.6、退出处理类
import com.alibaba.fastjson2.JSON;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 自定义退出处理类 返回成功
* 清除session
* @author 睡竹
*/
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
private static final Logger log = LoggerFactory.getLogger(LogoutSuccessHandlerImpl.class);
/**
* 退出处理
*
* @return
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
HttpSession session = request.getSession();
if (ObjectUtils.isNotEmpty(session)) {
//清空session
session.invalidate();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
//域名称
String userName = loginUser.getUser().getUserName();
//中文名
//String loginName = loginUser.getUser().getNickName();
// 记录用户退出日志
//AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
log.info("{}->退出成功",userName);
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error("0000", "退出成功")));
}
}
3.7、AjaxResult类
import java.util.HashMap;
/**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
/** 状态码 */
public static final String CODE_TAG = "retCode";
/** 返回内容 */
public static final String MSG_TAG = "retMsg";
/** 数据对象 */
public static final String DATA_TAG = "data";
/**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/
public AjaxResult() {
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/
public AjaxResult(String code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(String code, String msg, Object data) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data)) {
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success() {
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data) {
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg) {
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult("0000", msg, data);
}
/**
* 返回错误消息
*
* @return
*/
public static AjaxResult error() {
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult error(String msg, Object data) {
return new AjaxResult("9999", msg, data);
}
/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(String code, String msg) {
return new AjaxResult(code, msg, null);
}
/**
* 方便链式调用
*
* @param key 键
* @param value 值
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value) {
super.put(key, value);
return this;
}
}
3.8、session验证类,采用的是若依原来的token验证,所以类名没有修改
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* session验证
*
* @author farben163
* @date 2022年6月14日
* @telephone 15570718318
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private ISysUserService userService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
Object o = request.getSession().getAttribute("isLogin");
if (!ObjectUtils.isEmpty(o)) {
boolean islogin = (Boolean) o;
if (islogin) {
String username = request.getSession().getAttribute("userName").toString();
SysUser user = userService.selectUserByUserName(username);
LoginUser loginUser = new LoginUser(user.getUserId(), user);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null,
loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
chain.doFilter(request, response);
}
}
3.9、NoOpPasswordEncoder类
import org.springframework.security.crypto.password.PasswordEncoder;
public final class NoOpPasswordEncoder implements PasswordEncoder {
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
/**
* Get the singleton {@link NoOpPasswordEncoder}.
*/
public static PasswordEncoder getInstance() {
return INSTANCE;
}
private static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();
public NoOpPasswordEncoder() {
}
}
总结
上面代码可能有点乱,主要是部分代码不能直接放出来
整合ldap的关键代码主要在这
让ldap单独去验证账号密码,如果失败,就在UserDetailService中抛出失败异常,从而与springsecurity完美结合?
|