后端
1.添加CAS依赖
- 在common模块pom添加spring-security-cas依赖:
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-casartifactId>
dependency>
2.修改配置文件
- 在admin模块下的application.yml配置文件中添加:
cas:
server:
host:
url: http://host:port/sso
ticket_validator_url: http://host:port/sso
login_url: ${
cas.server.host.url}/login
logout_url: ${
cas.server.host.url}/logout?service=${
cas.server.host.url}/login?service=${
app.server.host.url}
app:
name: Xxx
casEnable: true
server:
host:
url: http://host:${
server.port}
login_url: /
logout_url: /logout
callback_url: /cas/index
web_url: http://host:port/xxx_vue
3.修改LoginUser.java
- 由于CAS认证需要authorities属性,此属性不能为空,此处为了方便直接new HashSet():
package com.ruoyi.common.core.domain.model;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
@Data
public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
private Long userId;
private Long deptId;
private String token;
private Long loginTime;
private Long expireTime;
private String ipaddr;
private String loginLocation;
private String browser;
private String os;
private Set<String> permissions;
private SysUser user;
private Map<String, Object> attributes;
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions, Map<String, Object> attributes) {
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
this.attributes = attributes;
}
@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;
}
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
return true;
}
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
@JSONField(serialize = false)
@Override
public boolean isEnabled()
{
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new HashSet<>();
}
}
4.修改 Constants.java
public static final String CAS_TOKEN = "cas_token";
public static final String WEB_TOKEN_KEY = "Admin-Token";
5.添加 CasProperties.java
- 在framework模块下添加读取cas配置信息:
package com.ruoyi.framework.config.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@Component
public class CasProperties {
@Value("${cas.server.host.url}")
private String casServerUrl;
@Value("${cas.server.host.ticket_validator_url}")
private String casServerTicketValidatorUrl;
@Value("${cas.server.host.login_url}")
private String casServerLoginUrl;
@Value("${cas.server.host.logout_url}")
private String casServerLogoutUrl;
@Value("${app.casEnable}")
private boolean casEnable;
@Value("${app.server.host.url}")
private String appServerUrl;
@Value("${app.login_url}")
private String appLoginUrl;
@Value("${app.logout_url}")
private String appLogoutUrl;
@Value("${app.callback_url}")
private String callbackUrl;
@Value("${app.web_url}")
private String webUrl;
}
6.添加 UserDetailsServiceCasImpl
package com.ruoyi.framework.web.service.impl;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class UserDetailsServiceCasImpl implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceCasImpl.class);
private final ISysUserService userService;
private final SysPermissionService permissionService;
public UserDetailsServiceCasImpl(ISysUserService userService, SysPermissionService permissionService) {
this.userService = userService;
this.permissionService = permissionService;
}
@Override
public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
String username = token.getName();
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
log.info("登录用户:{} 不存在。", username);
throw new ServiceException("登录用户:" + username + " 不存在。");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.info("登录用户:{} 已被删除。", username);
throw new ServiceException("对不起,您的账号:" + username + " 已被删除。");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登录用户:{} 已被停用。", username);
throw new ServiceException("对不起,您的账号:" + username + " 已停用。");
}
return createLoginUser(user, token.getAssertion().getPrincipal().getAttributes());
}
public UserDetails createLoginUser(SysUser user, Map<String, Object> attributes) {
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user), attributes);
}
}
7.添加 CasAuthenticationSuccessHandler.java
package com.ruoyi.framework.security.handle;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.framework.config.properties.CasProperties;
import com.ruoyi.framework.web.service.TokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Service
public class CasAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private static final Logger log = LoggerFactory.getLogger(CasAuthenticationSuccessHandler.class);
private static final RequestCache requestCache = new HttpSessionRequestCache();
private final RedisCache redisCache;
private final TokenService tokenService;
private final CasProperties casProperties;
@Value("${token.expireTime}")
private int expireTime;
public CasAuthenticationSuccessHandler(RedisCache redisCache, TokenService tokenService, CasProperties casProperties) {
this.redisCache = redisCache;
this.tokenService = tokenService;
this.casProperties = casProperties;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
LoginUser userDetails = (LoginUser) authentication.getPrincipal();
String token = tokenService.createToken(userDetails);
log.debug("CAS认证中心的ticket:"+authentication.getCredentials().toString());
redisCache.setCacheObject(CacheConstants.LOGIN_TICKET_KEY+authentication.getCredentials(