重构安全模块,移除oauth2.0,使用springsecurity代替
This commit is contained in:
parent
35da029ee4
commit
6c5e9884ba
|
|
@ -0,0 +1,41 @@
|
|||
package net.maku.security.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.constant.Constant;
|
||||
import net.maku.framework.security.user.UserDetail;
|
||||
import net.maku.system.enums.LoginOperationEnum;
|
||||
import net.maku.system.service.SysLogLoginService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 认证事件处理
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AuthenticationEvents {
|
||||
private final SysLogLoginService sysLogLoginService;
|
||||
|
||||
@EventListener
|
||||
public void onSuccess(AuthenticationSuccessEvent event) {
|
||||
// 用户信息
|
||||
UserDetail user = (UserDetail) event.getAuthentication().getPrincipal();
|
||||
|
||||
// 保存登录日志
|
||||
sysLogLoginService.save(user.getUsername(), Constant.SUCCESS, LoginOperationEnum.LOGIN_SUCCESS.getValue());
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onFailure(AbstractAuthenticationFailureEvent event) {
|
||||
// 用户名
|
||||
String username = (String) event.getAuthentication().getPrincipal();
|
||||
|
||||
// 保存登录日志
|
||||
sysLogLoginService.save(username, Constant.FAIL, LoginOperationEnum.ACCOUNT_FAIL.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package net.maku.security.filter;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.security.exception.FastAuthenticationException;
|
||||
import net.maku.framework.security.handler.UserAuthenticationFailureHandler;
|
||||
import net.maku.security.service.CaptchaService;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 验证码过滤器
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ValidateCodeFilter extends OncePerRequestFilter {
|
||||
private final static String OAUTH_TOKEN_URL = "/sys/oauth/token";
|
||||
private final CaptchaService captchaService;
|
||||
private final UserAuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
if(request.getServletPath().equals(OAUTH_TOKEN_URL)
|
||||
&& request.getMethod().equalsIgnoreCase("POST")
|
||||
&& "password".equalsIgnoreCase(request.getParameter("grant_type"))) {
|
||||
try {
|
||||
// 校验验证码
|
||||
validate(request);
|
||||
}catch (AuthenticationException e) {
|
||||
// 失败处理器
|
||||
authenticationFailureHandler.onAuthenticationFailure(request, response, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void validate(HttpServletRequest request) {
|
||||
String key = request.getParameter("key");
|
||||
String captcha = request.getParameter("captcha");
|
||||
|
||||
boolean flag = captchaService.validate(key, captcha);
|
||||
|
||||
if(!flag) {
|
||||
throw new FastAuthenticationException("验证码错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package net.maku.security.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.wf.captcha.SpecCaptcha;
|
||||
import com.wf.captcha.base.Captcha;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.utils.RedisKeys;
|
||||
import net.maku.framework.common.utils.RedisUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class CaptchaService {
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
* @param key key
|
||||
* @return 返回base64图片验证码
|
||||
*/
|
||||
public String generate(String key) {
|
||||
// 生成验证码
|
||||
SpecCaptcha captcha = new SpecCaptcha(150, 40);
|
||||
captcha.setLen(5);
|
||||
captcha.setCharType(Captcha.TYPE_DEFAULT);
|
||||
|
||||
// 保存到缓存
|
||||
key = RedisKeys.getCaptchaKey(key);
|
||||
redisUtils.set(key, captcha.text(), 300);
|
||||
|
||||
return captcha.toBase64();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码效验
|
||||
* @param key key
|
||||
* @param code 验证码
|
||||
* @return true:成功 false:失败
|
||||
*/
|
||||
public boolean validate(String key, String code) {
|
||||
if(StrUtil.isBlank(key) || StrUtil.isBlank(code)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
String captcha = getCache(key);
|
||||
|
||||
// 效验成功
|
||||
if(code.equalsIgnoreCase(captcha)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private String getCache(String key){
|
||||
key = RedisKeys.getCaptchaKey(key);
|
||||
String captcha = (String)redisUtils.get(key);
|
||||
// 删除验证码
|
||||
if(captcha != null){
|
||||
redisUtils.delete(key);
|
||||
}
|
||||
|
||||
return captcha;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
package net.maku.security.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.utils.JsonUtils;
|
||||
import net.maku.system.dao.SysOauthClientDao;
|
||||
import net.maku.system.entity.SysOauthClientEntity;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.ClientRegistrationException;
|
||||
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ClientDetailsService
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class FastClientDetailsService implements ClientDetailsService {
|
||||
private final SysOauthClientDao sysOauthClientDao;
|
||||
|
||||
@Override
|
||||
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
|
||||
SysOauthClientEntity oauthClient = sysOauthClientDao.getByClientId(clientId);
|
||||
|
||||
return clientDetailsMapper(oauthClient);
|
||||
}
|
||||
|
||||
private ClientDetails clientDetailsMapper(SysOauthClientEntity entity) {
|
||||
BaseClientDetails client = new BaseClientDetails();
|
||||
client.setClientId(entity.getClientId());
|
||||
// 密码前追加 {noop} 前缀,表示密码是明文
|
||||
client.setClientSecret(String.format("{noop}%s", entity.getClientSecret()));
|
||||
|
||||
if (ArrayUtil.isNotEmpty(entity.getAuthorizedGrantTypes())) {
|
||||
client.setAuthorizedGrantTypes(CollUtil.newArrayList(entity.getAuthorizedGrantTypes()));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getAuthorities())) {
|
||||
client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(entity.getAuthorities()));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getResourceIds())) {
|
||||
client.setResourceIds(StringUtils.commaDelimitedListToSet(entity.getResourceIds()));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getWebServerRedirectUri())) {
|
||||
client.setRegisteredRedirectUri(StringUtils.commaDelimitedListToSet(entity.getWebServerRedirectUri()));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getScope())) {
|
||||
client.setScope(StringUtils.commaDelimitedListToSet(entity.getScope()));
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getAutoapprove())) {
|
||||
client.setAutoApproveScopes(StringUtils.commaDelimitedListToSet(entity.getAutoapprove()));
|
||||
}
|
||||
|
||||
if (entity.getAccessTokenValidity() != null) {
|
||||
client.setAccessTokenValiditySeconds(entity.getAccessTokenValidity());
|
||||
}
|
||||
|
||||
if (entity.getRefreshTokenValidity() != null) {
|
||||
client.setRefreshTokenValiditySeconds(entity.getRefreshTokenValidity());
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(entity.getAdditionalInformation())) {
|
||||
Map<String, Object> map = JsonUtils.parseObject(entity.getAdditionalInformation(), Map.class);
|
||||
client.setAdditionalInformation(map);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package net.maku.security.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.exception.ErrorCode;
|
||||
import net.maku.framework.common.exception.FastException;
|
||||
import net.maku.framework.security.user.UserDetail;
|
||||
import net.maku.system.convert.SysUserConvert;
|
||||
import net.maku.system.dao.SysRoleDao;
|
||||
|
|
@ -13,18 +11,14 @@ import net.maku.system.enums.DataScopeEnum;
|
|||
import net.maku.system.enums.UserStatusEnum;
|
||||
import net.maku.system.service.SysMenuService;
|
||||
import net.maku.system.service.SysOrgService;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* UserDetailsService
|
||||
|
|
@ -43,15 +37,15 @@ public class FastUserDetailsService implements UserDetailsService {
|
|||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
SysUserEntity userEntity = sysUserDao.getByUsername(username);
|
||||
if(userEntity == null) {
|
||||
throw new FastException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
|
||||
if (userEntity == null) {
|
||||
throw new UsernameNotFoundException("用户名或密码错误");
|
||||
}
|
||||
|
||||
// 转换成UserDetail对象
|
||||
UserDetail userDetail = SysUserConvert.INSTANCE.convertDetail(userEntity);
|
||||
|
||||
// 账号不可用
|
||||
if(userEntity.getStatus() == UserStatusEnum.DISABLE.getValue()){
|
||||
if (userEntity.getStatus() == UserStatusEnum.DISABLE.getValue()) {
|
||||
userDetail.setEnabled(false);
|
||||
}
|
||||
|
||||
|
|
@ -60,15 +54,15 @@ public class FastUserDetailsService implements UserDetailsService {
|
|||
userDetail.setDataScopeList(dataScopeList);
|
||||
|
||||
// 用户权限列表
|
||||
Set<GrantedAuthority> authorities = getUserAuthority(userDetail);
|
||||
userDetail.setAuthorities(authorities);
|
||||
Set<String> authoritySet = sysMenuService.getUserAuthority(userDetail);
|
||||
userDetail.setAuthoritySet(authoritySet);
|
||||
|
||||
return userDetail;
|
||||
}
|
||||
|
||||
private List<Long> getDataScope(UserDetail userDetail){
|
||||
private List<Long> getDataScope(UserDetail userDetail) {
|
||||
Integer dataScope = sysRoleDao.getDataScopeByUserId(userDetail.getId());
|
||||
if (dataScope == null){
|
||||
if (dataScope == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
|
@ -98,14 +92,4 @@ public class FastUserDetailsService implements UserDetailsService {
|
|||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private Set<GrantedAuthority> getUserAuthority(UserDetail user) {
|
||||
// 获取用户权限标识
|
||||
Set<String> permsSet = sysMenuService.getUserAuthority(user);
|
||||
|
||||
// 封装权限标识
|
||||
Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
authorities.addAll(permsSet.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet()));
|
||||
|
||||
return authorities;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
package net.maku.security.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.utils.RedisKeys;
|
||||
import net.maku.framework.common.utils.RedisUtils;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 基于Redis的授权码模式
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class RedisAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
@Override
|
||||
protected void store(String code, OAuth2Authentication authentication) {
|
||||
String key = RedisKeys.getOauthCode(code);
|
||||
redisUtils.set(key, authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication remove(String code) {
|
||||
String key = RedisKeys.getOauthCode(code);
|
||||
return (OAuth2Authentication)redisUtils.get(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +1,5 @@
|
|||
auth:
|
||||
ignore_urls:
|
||||
- /actuator/**
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
- /swagger/**
|
||||
- /swagger-resources/**
|
||||
- /swagger-ui.html
|
||||
- /swagger-ui/**
|
||||
- /doc.html
|
||||
- /sys/oauth/captcha
|
||||
- /sys/auth/captcha
|
||||
- /sys/auth/login
|
||||
- /upload/**
|
||||
|
|
@ -1,73 +1,60 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>net.maku</groupId>
|
||||
<artifactId>fast-boot</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>fast-framework</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>net.maku</groupId>
|
||||
<artifactId>fast-boot</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>fast-framework</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>kotlin-stdlib-jdk7</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dameng</groupId>
|
||||
<artifactId>DmJdbcDriver18</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dameng</groupId>
|
||||
<artifactId>DmJdbcDriver18</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.maku.framework.common.utils;
|
||||
package net.maku.framework.common.cache;
|
||||
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
|
@ -10,38 +10,46 @@ import java.util.Map;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis工具类
|
||||
* Redis Cache
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
public class RedisUtils {
|
||||
public class RedisCache {
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
/** 默认过期时长为24小时,单位:秒 */
|
||||
/**
|
||||
* 默认过期时长为24小时,单位:秒
|
||||
*/
|
||||
public final static long DEFAULT_EXPIRE = 60 * 60 * 24L;
|
||||
/** 过期时长为1小时,单位:秒 */
|
||||
/**
|
||||
* 过期时长为1小时,单位:秒
|
||||
*/
|
||||
public final static long HOUR_ONE_EXPIRE = 60 * 60 * 1L;
|
||||
/** 过期时长为6小时,单位:秒 */
|
||||
/**
|
||||
* 过期时长为6小时,单位:秒
|
||||
*/
|
||||
public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L;
|
||||
/** 不设置过期时长 */
|
||||
/**
|
||||
* 不设置过期时长
|
||||
*/
|
||||
public final static long NOT_EXPIRE = -1L;
|
||||
|
||||
public void set(String key, Object value, long expire){
|
||||
public void set(String key, Object value, long expire) {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
if(expire != NOT_EXPIRE){
|
||||
if (expire != NOT_EXPIRE) {
|
||||
expire(key, expire);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(String key, Object value){
|
||||
public void set(String key, Object value) {
|
||||
set(key, value, DEFAULT_EXPIRE);
|
||||
}
|
||||
|
||||
public Object get(String key, long expire) {
|
||||
Object value = redisTemplate.opsForValue().get(key);
|
||||
if(expire != NOT_EXPIRE){
|
||||
if (expire != NOT_EXPIRE) {
|
||||
expire(key, expire);
|
||||
}
|
||||
return value;
|
||||
|
|
@ -51,11 +59,11 @@ public class RedisUtils {
|
|||
return get(key, NOT_EXPIRE);
|
||||
}
|
||||
|
||||
public Long increment(String key){
|
||||
public Long increment(String key) {
|
||||
return redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
public Boolean hasKey(String key){
|
||||
public Boolean hasKey(String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
|
|
@ -71,19 +79,19 @@ public class RedisUtils {
|
|||
return redisTemplate.opsForHash().get(key, field);
|
||||
}
|
||||
|
||||
public Map<String, Object> hGetAll(String key){
|
||||
public Map<String, Object> hGetAll(String key) {
|
||||
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
|
||||
return hashOperations.entries(key);
|
||||
}
|
||||
|
||||
public void hMSet(String key, Map<String, Object> map){
|
||||
public void hMSet(String key, Map<String, Object> map) {
|
||||
hMSet(key, map, DEFAULT_EXPIRE);
|
||||
}
|
||||
|
||||
public void hMSet(String key, Map<String, Object> map, long expire){
|
||||
public void hMSet(String key, Map<String, Object> map, long expire) {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
|
||||
if(expire != NOT_EXPIRE){
|
||||
if (expire != NOT_EXPIRE) {
|
||||
expire(key, expire);
|
||||
}
|
||||
}
|
||||
|
|
@ -95,32 +103,32 @@ public class RedisUtils {
|
|||
public void hSet(String key, String field, Object value, long expire) {
|
||||
redisTemplate.opsForHash().put(key, field, value);
|
||||
|
||||
if(expire != NOT_EXPIRE){
|
||||
if (expire != NOT_EXPIRE) {
|
||||
expire(key, expire);
|
||||
}
|
||||
}
|
||||
|
||||
public void expire(String key, long expire){
|
||||
public void expire(String key, long expire) {
|
||||
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void hDel(String key, Object... fields){
|
||||
public void hDel(String key, Object... fields) {
|
||||
redisTemplate.opsForHash().delete(key, fields);
|
||||
}
|
||||
|
||||
public void leftPush(String key, Object value){
|
||||
public void leftPush(String key, Object value) {
|
||||
leftPush(key, value, DEFAULT_EXPIRE);
|
||||
}
|
||||
|
||||
public void leftPush(String key, Object value, long expire){
|
||||
public void leftPush(String key, Object value, long expire) {
|
||||
redisTemplate.opsForList().leftPush(key, value);
|
||||
|
||||
if(expire != NOT_EXPIRE){
|
||||
if (expire != NOT_EXPIRE) {
|
||||
expire(key, expire);
|
||||
}
|
||||
}
|
||||
|
||||
public Object rightPop(String key){
|
||||
public Object rightPop(String key) {
|
||||
return redisTemplate.opsForList().rightPop(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package net.maku.framework.common.utils;
|
||||
package net.maku.framework.common.cache;
|
||||
|
||||
/**
|
||||
* Redis Key管理
|
||||
|
|
@ -15,10 +15,10 @@ public class RedisKeys {
|
|||
}
|
||||
|
||||
/**
|
||||
* 授权码Key
|
||||
* accessToken Key
|
||||
*/
|
||||
public static String getOauthCode(String code) {
|
||||
return "oauth:code:" + code;
|
||||
public static String getAccessTokenKey(String accessToken) {
|
||||
return "sys:access:" + accessToken;
|
||||
}
|
||||
|
||||
}
|
||||
33
fast-framework/src/main/java/net/maku/framework/security/cache/TokenStoreCache.java
vendored
Normal file
33
fast-framework/src/main/java/net/maku/framework/security/cache/TokenStoreCache.java
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package net.maku.framework.security.cache;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.common.cache.RedisCache;
|
||||
import net.maku.framework.common.cache.RedisKeys;
|
||||
import net.maku.framework.security.user.UserDetail;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 认证 Cache
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class TokenStoreCache {
|
||||
private final RedisCache redisCache;
|
||||
|
||||
public void saveUser(String accessToken, UserDetail user) {
|
||||
String key = RedisKeys.getAccessTokenKey(accessToken);
|
||||
redisCache.set(key, user);
|
||||
}
|
||||
|
||||
public UserDetail getUser(String accessToken) {
|
||||
String key = RedisKeys.getAccessTokenKey(accessToken);
|
||||
return (UserDetail) redisCache.get(key);
|
||||
}
|
||||
|
||||
public void deleteUser(String accessToken) {
|
||||
String key = RedisKeys.getAccessTokenKey(accessToken);
|
||||
redisCache.delete(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package net.maku.framework.security.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.security.exception.FastWebResponseExceptionTranslator;
|
||||
import net.maku.framework.security.token.FastTokenEnhancer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
|
||||
/**
|
||||
* 认证服务器配置
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableAuthorizationServer
|
||||
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final ClientDetailsService fastClientDetailsService;
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final AuthorizationCodeServices redisAuthorizationCodeServices;
|
||||
private final TokenStore tokenStore;
|
||||
|
||||
/**
|
||||
* 配置客户端信息
|
||||
*/
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
clients.withClientDetails(fastClientDetailsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE);
|
||||
// 密码模式
|
||||
endpoints.authenticationManager(authenticationManager);
|
||||
// 支持刷新令牌
|
||||
endpoints.userDetailsService(userDetailsService);
|
||||
// 令牌管理
|
||||
endpoints.tokenStore(tokenStore);
|
||||
// 令牌增强
|
||||
endpoints.tokenEnhancer(tokenEnhancer());
|
||||
// 登录或者鉴权失败时的返回信息
|
||||
endpoints.exceptionTranslator(new FastWebResponseExceptionTranslator());
|
||||
// 配置授权码模式,存放在Redis中
|
||||
endpoints.authorizationCodeServices(redisAuthorizationCodeServices);
|
||||
// 自定义登录地址
|
||||
endpoints.pathMapping("/oauth/token","/sys/oauth/token");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenEnhancer tokenEnhancer() {
|
||||
return new FastTokenEnhancer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerSecurityConfigurer security) {
|
||||
security
|
||||
.allowFormAuthenticationForClients()
|
||||
.tokenKeyAccess("permitAll()") // 匿名可访问/oauth/token_key
|
||||
.checkTokenAccess("isAuthenticated()") // 认证后可访问/oauth/check_token
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,10 +21,10 @@ import java.util.Properties;
|
|||
@Component
|
||||
public class PermitResource {
|
||||
/**
|
||||
* 指定被 spring security oauth2.0 忽略的URL
|
||||
* 指定被 spring security 忽略的URL
|
||||
*/
|
||||
@SneakyThrows
|
||||
public List<String> getPermitList(){
|
||||
public List<String> getPermitList() {
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
Resource[] resources = resolver.getResources("classpath*:auth.yml");
|
||||
String key = "auth.ignore_urls";
|
||||
|
|
@ -32,16 +32,16 @@ public class PermitResource {
|
|||
return getPropertiesList(key, resources);
|
||||
}
|
||||
|
||||
private List<String> getPropertiesList(String key, Resource... resources){
|
||||
private List<String> getPropertiesList(String key, Resource... resources) {
|
||||
List<String> list = new ArrayList<>();
|
||||
|
||||
// 解析资源文件
|
||||
for(Resource resource : resources) {
|
||||
for (Resource resource : resources) {
|
||||
Properties properties = loadYamlProperties(resource);
|
||||
|
||||
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||
String tmpKey = StringUtils.substringBefore(entry.getKey().toString(), "[");
|
||||
if(tmpKey.equalsIgnoreCase(key)){
|
||||
if (tmpKey.equalsIgnoreCase(key)) {
|
||||
list.add(entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
package net.maku.framework.security.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.security.exception.SecurityAuthenticationEntryPoint;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 资源服务器配置
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableResourceServer
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
|
||||
private final TokenStore tokenStore;
|
||||
private final PermitResource permitResource;
|
||||
|
||||
@Override
|
||||
public void configure(ResourceServerSecurityConfigurer resources) {
|
||||
resources.tokenStore(tokenStore);
|
||||
|
||||
resources.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
// 忽略授权的地址列表
|
||||
List<String> permitList = permitResource.getPermitList();
|
||||
String [] permits = permitList.toArray(new String[permitList.size()]);
|
||||
|
||||
http
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(permits).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package net.maku.framework.security.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.security.exception.SecurityAuthenticationEntryPoint;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
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.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class SecurityConfig {
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final OncePerRequestFilter authenticationTokenFilter;
|
||||
private final PermitResource permitResource;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// 忽略授权的地址列表
|
||||
List<String> permitList = permitResource.getPermitList();
|
||||
String[] permits = permitList.toArray(new String[0]);
|
||||
|
||||
http
|
||||
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and().authorizeRequests()
|
||||
.antMatchers(permits).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and().userDetailsService(userDetailsService)
|
||||
.exceptionHandling().authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
|
||||
.and().headers().frameOptions().disable()
|
||||
.and().csrf().disable()
|
||||
;
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return web -> web.ignoring().antMatchers(HttpMethod.OPTIONS);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package net.maku.framework.security.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
|
||||
|
||||
/**
|
||||
* TokenStore
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
public class TokenStoreConfig {
|
||||
private final RedisConnectionFactory redisConnectionFactory;
|
||||
|
||||
@Bean
|
||||
public TokenStore tokenStore() {
|
||||
// 使用redis存储token
|
||||
return new RedisTokenStore(redisConnectionFactory);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
package net.maku.framework.security.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* Spring Security配置
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
private final UserDetailsService userDetailsService;
|
||||
private final OncePerRequestFilter validateCodeFilter;
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码模式需要
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception{
|
||||
return super.authenticationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/login")
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/oauth/authorize").authenticated()
|
||||
.anyRequest().permitAll()
|
||||
.and().headers().frameOptions().disable()
|
||||
.and().csrf().disable()
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
web.ignoring().antMatchers(HttpMethod.OPTIONS);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package net.maku.framework.security.exception;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
/**
|
||||
* 认证异常类
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class FastAuthenticationException extends AuthenticationException {
|
||||
public FastAuthenticationException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
public FastAuthenticationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package net.maku.framework.security.exception;
|
||||
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class FastOAuth2Exception extends OAuth2Exception {
|
||||
private String msg;
|
||||
|
||||
public FastOAuth2Exception(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public FastOAuth2Exception(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package net.maku.framework.security.exception;
|
||||
|
||||
import net.maku.framework.common.exception.ErrorCode;
|
||||
import net.maku.framework.common.utils.Result;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
|
||||
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||
import org.springframework.security.web.util.ThrowableAnalyzer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
|
||||
/**
|
||||
* 登录或者鉴权失败时的返回信息
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
public class FastWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {
|
||||
private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
|
||||
|
||||
@Override
|
||||
public ResponseEntity<OAuth2Exception> translate(Exception e) {
|
||||
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
|
||||
|
||||
Exception exception = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
|
||||
if (exception != null) {
|
||||
return handleOAuth2Exception(new FastOAuth2Exception(e.getMessage(), e));
|
||||
}
|
||||
|
||||
exception = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
|
||||
if (exception != null) {
|
||||
return handleOAuth2Exception(new FastOAuth2Exception(exception.getMessage(), exception));
|
||||
}
|
||||
|
||||
exception = (InvalidGrantException) throwableAnalyzer.getFirstThrowableOfType(InvalidGrantException.class, causeChain);
|
||||
if (exception != null) {
|
||||
return handleOAuth2Exception(new FastOAuth2Exception(exception.getMessage(), exception));
|
||||
}
|
||||
|
||||
exception = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
|
||||
if (exception != null) {
|
||||
return handleOAuth2Exception(new FastOAuth2Exception(exception.getMessage(), exception));
|
||||
}
|
||||
|
||||
exception = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
|
||||
if (exception != null) {
|
||||
return handleOAuth2Exception((OAuth2Exception) exception);
|
||||
}
|
||||
|
||||
return handleOAuth2Exception(new FastOAuth2Exception(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
|
||||
}
|
||||
|
||||
private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) {
|
||||
int status = e.getHttpErrorCode();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set(HttpHeaders.CACHE_CONTROL, "no-store");
|
||||
headers.set(HttpHeaders.PRAGMA, "no-cache");
|
||||
if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
|
||||
headers.set(HttpHeaders.WWW_AUTHENTICATE, String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
|
||||
}
|
||||
|
||||
if (e instanceof ClientAuthenticationException) {
|
||||
return new ResponseEntity<>(e, headers, HttpStatus.valueOf(status));
|
||||
}
|
||||
Result<String> result = Result.error(ErrorCode.ACCOUNT_PASSWORD_ERROR);
|
||||
|
||||
return new ResponseEntity(result, headers, HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package net.maku.framework.security.filter;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.maku.framework.security.cache.TokenStoreCache;
|
||||
import net.maku.framework.security.user.UserDetail;
|
||||
import net.maku.framework.security.utils.TokenUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 认证过滤器
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
private final TokenStoreCache tokenStoreCache;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
|
||||
String accessToken = TokenUtils.getAccessToken(request);
|
||||
// accessToken为空,表示未登录
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取登录用户信息
|
||||
UserDetail user = tokenStoreCache.getUser(accessToken);
|
||||
if (user == null) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 用户存在
|
||||
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
|
||||
|
||||
// 新建 SecurityContext
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
context.setAuthentication(authentication);
|
||||
SecurityContextHolder.setContext(context);
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package net.maku.framework.security.handler;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.maku.framework.common.utils.HttpContextUtils;
|
||||
import net.maku.framework.common.utils.JsonUtils;
|
||||
import net.maku.framework.common.utils.Result;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 认证失败处理器
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Component
|
||||
public class UserAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
|
||||
|
||||
response.getWriter().write(JsonUtils.toJsonString(Result.error(e.getMessage())));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package net.maku.framework.security.token;
|
||||
|
||||
import net.maku.framework.common.utils.Result;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 令牌
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class FastTokenEnhancer implements TokenEnhancer {
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
|
||||
if(accessToken instanceof DefaultOAuth2AccessToken){
|
||||
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
|
||||
|
||||
// 增加额外信息
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("code", new Result<>().getCode());
|
||||
token.setAdditionalInformation(info);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
package net.maku.framework.security.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
|
|
@ -29,10 +31,10 @@ public class UserDetail implements UserDetails {
|
|||
private Long orgId;
|
||||
private Integer status;
|
||||
private Integer superAdmin;
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 数据权限范围
|
||||
*
|
||||
* <p>
|
||||
* null:表示全部数据权限
|
||||
*/
|
||||
private List<Long> dataScopeList;
|
||||
|
|
@ -55,11 +57,12 @@ public class UserDetail implements UserDetails {
|
|||
/**
|
||||
* 拥有权限集合
|
||||
*/
|
||||
private Set<GrantedAuthority> authorities;
|
||||
private Set<String> authoritySet;
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return this.authorities;
|
||||
return authoritySet.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package net.maku.framework.security.utils;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Token 工具类
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class TokenUtils {
|
||||
|
||||
/**
|
||||
* 生成 AccessToken
|
||||
*/
|
||||
public static String generator() {
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 AccessToken
|
||||
*/
|
||||
public static String getAccessToken(HttpServletRequest request) {
|
||||
String accessToken = request.getHeader("Authorization");
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
accessToken = request.getParameter("access_token");
|
||||
}
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
auth:
|
||||
ignore_urls:
|
||||
- /actuator/**
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
- /swagger/**
|
||||
- /swagger-resources/**
|
||||
- /swagger-ui.html
|
||||
- /swagger-ui/**
|
||||
- /doc.html
|
||||
Loading…
Reference in New Issue
Block a user