大家平时在写代码的时候,安全方面一般都会考虑使用Shiro或者SpringSecurity,他们其中提供了很多注解可以直接使用,很方便,那么今天就来重复造个小轮子,如果不用他们的,自己在项目中如何基于注解利用SpEl表达式来控制权限呢。下面我们上代码
首先我们定义了一个异常类,代码如下:
public class NoPermissionException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 6262010463678812588L;
public NoPermissionException() {
super();
}
/** Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public NoPermissionException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public NoPermissionException(String message, Throwable cause) {
super(message, cause);
}
}
在看看我们的自定义注解,这个注解直接复制的SpringSecurity中的注解
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xzm.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for specifying a method access-control expression which will be evaluated to
* decide whether a method invocation is allowed or not.
*
* @author Luke Taylor
* @since 3.0
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
/**
* @return the Spring-EL expression to be evaluated before invoking the protected
* method
*/
String value();
}
Controller层的代码如下:
@RequestMapping(value = "/index")
@PreAuthorize("#msg == 'abc'")
public List<User> helloApi(String msg) {
List<User> users = userMapper.selectList();
System.out.println(users);
return users;
}
下面我们来看AOP层面的代码定义
package com.xzm.aop;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import com.xzm.annotations.PreAuthorize;
import com.xzm.exceptions.NoPermissionException;
@Aspect
@Component
public class SpelEnhance {
/**
* 用于SpEL表达式解析.
*/
private SpelExpressionParser parser = new SpelExpressionParser();
/**
* 用于获取方法参数定义名字.
*/
private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
@Around("@annotation(com.xzm.annotations.PreAuthorize)")
public Object around(ProceedingJoinPoint p) throws Throwable {
MethodSignature sign = (MethodSignature) p.getSignature();
Method method = sign.getMethod();
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
if (preAuthorize == null) {
return p.proceed();
}
String value = preAuthorize.value();
String resV = generateKeyBySpEL(value, p);
if (resV.equals("true")) {
return p.proceed();
}
throw new NoPermissionException("无权限访问");
}
public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
Expression expression = parser.parseExpression(spELString);
EvaluationContext context = new StandardEvaluationContext();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
return expression.getValue(context).toString();
}
}
以上基本上就完了,主要是这段代码
public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
Expression expression = parser.parseExpression(spELString);
EvaluationContext context = new StandardEvaluationContext();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
return expression.getValue(context).toString();
}
用来解析SpEl表达式,解析成功后把结果返回。有兴趣的可以在下面评论,技术问题可以私聊我