spring boot 整合vue自定义权限

介绍(原创转载请注明地址)

  • 将权限控制放到后台处理(权限为最简单的),采用spring-session和redis,cookie,如果怕sessionId劫持采用加密算法。
  • session和cookie混合使用由服务器session(将session接入到redis)添加用户信息,提供给客户端cookie一个token,通过token(session读取建议加密读取)读取session。权限采用自定义注解。
  • token

maven

 


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
     
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-jdbc</artifactId>
        </dependency>
        <!-- java的工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!--加密算法(spring提供的加密算法不安全只有MD5和base64)-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

application.yml配置文件

  redis:
    host: 0.0.0.0
    password: 0000000
    timeout: PT30M
    #spring-session
  session:
    store-type: redis
    timeout: PT30M
#tocmat 超时默认30分钟
server:
  servlet:
    context-path: /
    session:
      timeout: PT30M
  tomcat:
    uri-encoding: UTF-8
#超时时间要保持一致

一,密码加密

加密算法常用有MD5,BASE64,SHA-1,SHA-2(SHA:Secure Hash Algorithm即安全散列算法),HMAC(Message Authentication Code消息认证码和MD5,SHA一起使用)。除了BASE64可以解码,其它不可解码,MD5不安全是因为可以碰撞出来,这里不细说了,密码采用HMAC-SHA-2最好(算法有HMAC_MD5,HMAC_SHA_1,(后面为SHA-2算法)HMAC_SHA_224,HMAC_SHA_384,HMAC_SHA_256,HMAC_SHA_256,HMAC_SHA_512)。

这里只说一种算法HMAC_SHA_256算法(密码加密最好采用多算法加密)

/**
 * 加密
 */
public class MyUtils {


    private MyUtils() {
    }


    /**
     * 编码base64
     *
     * @param data 需要编码的数据
     * @return 编码之后的内容
     */
    public static String encodeBase64(Object data) {
        try {
            if (Objects.nonNull(data)) {
                String json =getJson(data);
                if (StringUtils.isNotEmpty(json)) {
                    return Base64.encodeBase64String(json.getBytes(StandardCharsets.UTF_8));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解码base64
     *
     * @param data 解码的内容
     * @return 解码之后的数据
     */
    public static String decodeBase64(Object data) {
        try {
            String json =getJson(data);
            if (StringUtils.isNotEmpty(json)) {
                return new String(Base64.decodeBase64(json));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 单加密
     *
     * @param pwd 需要加密的内容
     * @return string
     */
    public static String encryptToken(String pwd) {
        //防止碰撞加了内容:1sdfjjthu235
        return new HmacUtils(HmacAlgorithms.HMAC_SHA_256, "123456").hmacHex(pwd)+"1sdfjjthu235";
       //hmac和hamcHex区别是加密的内容一个是8进制一个是16进制
       //123456为密匙不要泄密(一般是数据库提供的密匙)
    }

    /**
     * 单校验
     *
     * @param pwd 需要加密的内容
     * @return string
     */
    public static boolean verifyToken(String pwd, String data) {
        boolean flag = false;
        if (StringUtils.isNoneEmpty(pwd, data)) {
            if (Objects.equals(pwd, encryptToken(data))) {
                flag = true;
            }
        }
        return flag;
    }
     public static String getJson(Object data) {
        try {
           if (Objects.nonNull(data)){
               ObjectMapper mapper = new ObjectMapper();
               return mapper.writeValueAsString(data);
           }
        } catch (JsonProcessingException e) {
            logger.error("JSON转换异常",e.getMessage());
        }
        return null;
    }
}

工具类

public class HttpUtils {
    private HttpUtils() {

    }

    private final static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private static RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


    public static HttpServletRequest getHttpServletRequest() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            if (Objects.nonNull(attributes)) {
                return attributes.getRequest();
            }
        } catch (Exception e) {
            logger.error("获取httpRequest异常:" + e.getMessage());
        }
        return null;
    }

    public static HttpServletResponse getHttpServletResponse() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            if (Objects.nonNull(attributes)) {
                return attributes.getResponse();
            }
        } catch (Exception e) {
            logger.error("获取httpResponse异常:" + e.getMessage());
        }
        return null;
    }
}
public class SessionUtils {
    private SessionUtils() {
    }

    /**
     * 修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,
     * 例如name、path、domain等,都要与原Cookie完全一样。
     * 否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
     */

    private final static Logger logger = LoggerFactory.getLogger(SessionUtils.class);


    private static HttpServletRequest httpServletRequest = HttpUtils.getHttpServletRequest();


    public static Object getSession(String key) {
        try {
            if (StringUtils.isNotEmpty(key)) {
                if (Objects.nonNull(httpServletRequest)) {
                    HttpSession session = httpServletRequest.getSession();
                    return session.getAttribute(key);
                }
            }
        } catch (Exception e) {
            logger.error("session添加异常!", e.getMessage());
        }
        return "session获取的key为空!";
    }

    public static void save(String key, Object data) {
        try {
            if (Objects.nonNull(httpServletRequest)) {
                HttpSession session = httpServletRequest.getSession();
                session.setAttribute(key, data);
            }
        } catch (Exception e) {
            logger.error("session添加失败!", e.getMessage());
        }
    }

    public static void delete(String key) {
        try {
            if (Objects.nonNull(httpServletRequest)) {
                HttpSession session = httpServletRequest.getSession();
                session.removeAttribute(key);
            }
        } catch (Exception e) {
            logger.error("session删除失败!", e.getMessage());
        }
    }
}
public class CookiesUtils {

    public static final String TOKEN = "token";

    private CookiesUtils() {

    }

    /**
     * 修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,
     * 例如name、path、domain等,都要与原Cookie完全一样。
     * 否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
     */

    private final static Logger logger = LoggerFactory.getLogger(CookiesUtils.class);

    private static HttpServletResponse httpServletResponse = HttpUtils.getHttpServletResponse();

    private static HttpServletRequest httpServletRequest = HttpUtils.getHttpServletRequest();


    public static String getCookie(String key) {
        try {
            if (StringUtils.isNotEmpty(key)) {
                if (Objects.nonNull(httpServletRequest)) {
                    for (Cookie cookie : httpServletRequest.getCookies()) {
                        if (Objects.equals(cookie.getName(), key)) {
                            return cookie.getValue();
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("cookie获取异常!", e.getMessage());
        }
        return "cookie获取的token为空!";
    }

    public static void save(String key, Object data) {
        try {
            if (Objects.nonNull(httpServletResponse)) {
                Cookie cookie = new Cookie(key, String.valueOf(data));
                //设置cookie有效时间为30分钟
                cookie.setMaxAge(30 * 60);
                //禁止js脚本读取cookie必须通过接口读取防止XSS攻击
                cookie.setHttpOnly(true);
                httpServletResponse.addCookie(cookie);
            }
        } catch (Exception e) {
            logger.error("cookie添加失败!", e.getMessage());
        }
    }

    public static void delete(String key) {
        try {
            if (Objects.nonNull(httpServletRequest)) {
                Cookie[] cookies = httpServletRequest.getCookies();
                for (Cookie cookie : cookies) {
                    if (Objects.equals(cookie.getName(), key)) {
                        cookie.setValue(null);
                        cookie.setMaxAge(0);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("cookie删除失败!", e.getMessage());
        }
    }

    public static boolean exists(String key) {
        boolean flag = false;
        try {
            if (Objects.nonNull(httpServletRequest)) {
                Cookie[] cookies = httpServletRequest.getCookies();
                for (Cookie cookie : cookies) {
                    if (Objects.equals(cookie.getName(), key)) {
                        flag = true;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("exists-cookie异常!", e.getMessage());
        }
        return flag;
    }
}

 

登录的service实现类 

public boolean login(User user) {
        try {
            if (StringUtils.isNoneEmpty(user.getUserName(), user.getPwd())) {
                //根据用户名查询 pwd负责接收前台敏文密码password是数据库加密后的密码
                User users = UserRepository.getByUserName(user.getUserName());
                if (Objects.nonNull(user)) {
                    //这里是根据角色查询权限user.setPermissions(permissionService.getPermissions(user.getRoles()));
                    if (StringUtils.isNoneEmpty(users.getPassword())) {
                        //算法校验
                        boolean flag = CryptUtils.verify(users.getPassword(),user.getPwd());
                        if (flag) {
                            //session和cookie操作
                            //建议token加密(思路为cookie的值不加密,session的key是由cookie提供的值进行加密得到key)
                            String token = 保证唯一,可以使用数据库唯一列,例如用户名;
                            if (CookiesUtils.exists(token)) {
                                CookiesUtils.delete(token);
                                SessionUtils.delete(token);
                            } else {
                                CookiesUtils.save(CookiesUtils.TOKEN, token);
                                SessionUtils.save(token, user);
                            }
                            return true;
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("登录异常!", e.getMessage());
        }
        return false;
    }

权限

public class UserUtils {
    private UserUtils() {
    }

    public static User get() {
        try {
            String token = CookiesUtils.getCookie(CookiesUtils.TOKEN);
            if (StringUtils.isNotEmpty(token)) {
                //建议token加密参考登录
                return (User) SessionUtils.getSession(token);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
/**
 * 授权注解
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
    String value() default "";
}

 

/**
 * 权限拦截器
 */
public class SecurityInterceptor extends HandlerInterceptorAdapter {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        boolean flag = false;
        try {
            HttpStatus httpStatus = hasPermission(handler);
            String methodClass = ((HandlerMethod) handler).getMethod().getDeclaringClass().getName();
            String methodName = ((HandlerMethod) handler).getMethod().getName();
            switch (httpStatus) {
                case OK:
                    //认证成功放行。
                    flag = true;
                    break;
                case UNAUTHORIZED:
                    //如果没有权限响应401未授权
                    response.sendError(HttpStatus.UNAUTHORIZED.value(), "Message:unauthorized");
                    logger.warn("401未授权!未授权的接口:" + methodClass + "." + methodName);
                    break;
                case FORBIDDEN:
                    //如果未登录请求则响应403被禁用
                    response.sendError(HttpStatus.FORBIDDEN.value(), "Message:forbidden");
                    logger.warn("403禁止访问!");
                    break;
            }
        } catch (Exception e) {
            logger.error("权限认证异常!", e.getMessage());
        }
        return flag;
    }

   /**
     * 是否有权限
     *
     * @param handler handler
     * @return boolean
     */
    private HttpStatus hasPermission(Object handler) {
        //默认状态码为403(被禁止)
        HttpStatus httpStatus = HttpStatus.FORBIDDEN;
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取方法上的注解
            PreAuthorize preAuthorize = handlerMethod.getMethod().getAnnotation(PreAuthorize.class);
            // 如果方法上的注解为空 则获取类的注解
            if (Objects.isNull(preAuthorize)) {
                preAuthorize = handlerMethod.getMethod().getDeclaringClass().getAnnotation(PreAuthorize.class);
            }
            // 如果标记了注解,则判断权限
            if (Objects.nonNull(preAuthorize) && StringUtils.isNotBlank(preAuthorize.value())) {
                // redis或数据库 中获取该用户的权限信息 并判断是否有权限
                SysUser user = UserUtils.get();
                if (Objects.nonNull(user)) {
                    for (SysRole role : user.getSysRoles()) {
                        if (Objects.nonNull(role.getSysPermissions())) {
                            for (SysPermission permission : role.getSysPermissions()) {
                                if (StringUtils.isNotBlank(permission.getPerCode())) {
                                    while (Objects.equals(permission.getPerCode(), preAuthorize.value())){
                                        //返回状态码为200(成功)
                                        httpStatus = HttpStatus.OK;
                                    }
                                } else {
                                    //返回状态码为401(未授权)
                                    httpStatus = HttpStatus.UNAUTHORIZED;
                                }
                            }
                        }
                    }
                }
            }
        }
        return httpStatus;
    }
}
@Configuration
public class MVCConfig extends WebMvcConfigurationSupport {
    @Bean
    public SecurityInterceptor getSecurityInterceptor() {
        return new SecurityInterceptor();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        InterceptorRegistration registration = registry.addInterceptor(getSecurityInterceptor());
        //排除路径
        registration.excludePathPatterns("/login");
        registration.excludePathPatterns("/logout");
        //注意必须放行spring boot 提供的默认错误controller
        registration.excludePathPatterns("/error");
        //拦截全部路径
        registration.addPathPatterns("/**");
    }
}

注解用法

    @PreAuthorize("user@create")要和数据库字段permission的renZhen保证一样
    @GetMapping("create")
    public String create(){
        return "我是授权过的!";
    }

Vue通过Axios处理401,403,404,500即可。cookie和session和前台无关,由后台自行处理

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值