增加 auth 授权相关处理(未完成)

This commit is contained in:
YunaiV
2020-04-22 21:57:26 +08:00
parent 6f37500f62
commit a545d673ab
36 changed files with 528 additions and 54 deletions

View File

@@ -6,7 +6,6 @@ import cn.iocoder.mall.web.core.constant.CommonMallConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -22,16 +21,22 @@ public class CommonSecurityAutoConfiguration implements WebMvcConfigurer {
// ========== 拦截器相关 ==========
@Bean
@ConditionalOnMissingBean(AccountAuthInterceptor.class)
public AccountAuthInterceptor accountAuthInterceptor() {
return new AccountAuthInterceptor();
public AccountAuthInterceptor adminAccountAuthInterceptor() {
return new AccountAuthInterceptor(true);
}
@Bean
public AccountAuthInterceptor userAccountAuthInterceptor() {
return new AccountAuthInterceptor(false);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// AccountAuthInterceptor 拦截器
registry.addInterceptor(this.accountAuthInterceptor())
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**", CommonMallConstants.ROOT_PATH_USER + "/**");
registry.addInterceptor(this.userAccountAuthInterceptor())
.addPathPatterns(CommonMallConstants.ROOT_PATH_USER + "/**");
registry.addInterceptor(this.adminAccountAuthInterceptor())
.addPathPatterns(CommonMallConstants.ROOT_PATH_ADMIN + "/**");
logger.info("[addInterceptors][加载 AccountAuthInterceptor 拦截器完成]");
}

View File

@@ -3,14 +3,16 @@ package cn.iocoder.mall.security.core.annotation;
import java.lang.annotation.*;
/**
* 要求用户登录注解通过将该注解添加到 Controller 会自动校验用户是否登陆
* 要求用户认证登陆注解通过将该注解添加到 Controller 会自动校验用户是否登陆
*
* 默认请求下用户访问的 API 接口无需登陆主要的考虑是
* 1. 需要用户登陆的接口本身会获取在线用户的编号如果不添加 @RequiresLogin 注解就会报错
* 2. 大多数情况下用户的 API 接口无需登陆
*
* ps同样适用于管理员 Admin
*/
@Documented
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE 因为没有场景
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresLogin {
public @interface RequiresAuthenticate {
}

View File

@@ -0,0 +1,12 @@
package cn.iocoder.mall.security.core.annotation;
import java.lang.annotation.*;
/**
* 通过将该注解添加到 Controller 的方法上,声明无需进行登陆
*/
@Documented
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresNone {
}

View File

@@ -6,6 +6,8 @@ import java.lang.annotation.*;
* 参考 Shiro @RequiresPermissions 设计 http://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/authz/annotation/RequiresPermissions.html
*
* 通过将该注解添加到 Controller 的方法上,进行授权鉴定
*
* ps目前暂时只有管理员 Admin 使用到
*/
@Documented
@Target({ElementType.METHOD}) // 暂时不支持 ElementType.TYPE ,因为没有场景

View File

@@ -3,10 +3,8 @@ package cn.iocoder.mall.security.core.context;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Set;
/**
* Security 上下文
* Admin Security 上下文
*/
@Data
@Accessors(chain = true)
@@ -20,9 +18,5 @@ public class AdminSecurityContext {
* 管理员账号
*/
private String username;
/**
* 拥有的角色编号
*/
private Set<Integer> roleIds;
}

View File

@@ -1,8 +1,13 @@
package cn.iocoder.mall.security.core.interceptor;
import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.core.annotation.RequiresAuthenticate;
import cn.iocoder.mall.security.core.annotation.RequiresNone;
import cn.iocoder.mall.security.core.annotation.RequiresPermissions;
import cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum;
import cn.iocoder.mall.system.rpc.api.oauth2.OAuth2RPC;
import cn.iocoder.mall.system.rpc.request.oauth2.OAuth2AccessTokenAuthenticateRequest;
import cn.iocoder.mall.system.rpc.response.oauth2.OAuth2AccessTokenResponse;
@@ -11,6 +16,7 @@ import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
@@ -23,12 +29,36 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
@Reference(validation = "true", version = "${dubbo.consumer.OAuth2RPC.version}")
private OAuth2RPC oauth2RPC;
/**
* 是否默认要求认证
*
* 针对 /users/** 接口,一般默认不要求认证,因为面向用户的接口,往往不需要登陆即可访问
* 针对 /admins/** 接口,一般默认要求认证,因为面向管理员的接口,往往是内部需要更严格的安全控制
*/
private final boolean defaultRequiresAuthenticate;
public AccountAuthInterceptor(boolean defaultRequiresAuthenticate) {
this.defaultRequiresAuthenticate = defaultRequiresAuthenticate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 获得访问令牌
String accessToken = HttpUtil.obtainAuthorization(request);
if (StringUtils.hasText(accessToken)) { // 如果未传递,则不进行认证
return true;
// 1. 进行认证
Integer accountId = this.obtainAccount(request);
// 2. 进行鉴权
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 判断是否需要认证
this.checkAuthenticate(handlerMethod, accountId);
// 判断是否需要权限
return true;
}
private Integer obtainAccount(HttpServletRequest request) {
String accessToken = HttpUtil.obtainAuthorization(request); // 获得访问令牌
if (!StringUtils.hasText(accessToken)) { // 如果未传递,则不进行认证
return null;
}
// 执行认证
OAuth2AccessTokenAuthenticateRequest oauth2AccessTokenAuthenticateRequest = new OAuth2AccessTokenAuthenticateRequest()
@@ -38,8 +68,35 @@ public class AccountAuthInterceptor extends HandlerInterceptorAdapter {
throw ServiceExceptionUtil.exception(oauth2AccessTokenResponseResult);
}
// 设置账号编号
CommonWebUtil.setAccountId(request, oauth2AccessTokenResponseResult.getData().getAccountId());
return true;
Integer accountId = oauth2AccessTokenResponseResult.getData().getAccountId();
CommonWebUtil.setAccountId(request, accountId);
return accountId;
}
private void checkAuthenticate(HandlerMethod handlerMethod, Integer accountId) {
boolean requiresAuthenticate = defaultRequiresAuthenticate;
if (handlerMethod.hasMethodAnnotation(RequiresAuthenticate.class)
|| handlerMethod.hasMethodAnnotation(RequiresPermissions.class)) { // 如果需要权限验证,也认为需要认证
requiresAuthenticate = true;
} else if (handlerMethod.hasMethodAnnotation(RequiresNone.class)) {
requiresAuthenticate = false;
}
if (requiresAuthenticate && accountId == null) {
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.OAUTH2_NOT_AUTHENTICATE);
}
}
private void checkPermission(HandlerMethod handlerMethod, Integer accountId) {
RequiresPermissions requiresPermissions = handlerMethod.getMethodAnnotation(RequiresPermissions.class);
if (requiresPermissions == null) {
return;
}
String[] permissions = requiresPermissions.value();
if (CollectionUtil.isEmpty(permissions)) {
return;
}
// 权限验证
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.security.core.interceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获得 Admin 信息
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.security.core.interceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获得用户信息
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
}