【SpringBoot】运用拦截器与注解,在SpringBoot中实现自定义的权限认证

                        运用拦截器与注解,在SpringBoot中实现自定义的权限认证

    权限的认证框架很多,比如Shiro与SpringSecurity。今天使用拦截器与注解的方式,实现一个自定义的权限认证。

目前,系统中需要两种角色,分别是平台管理员与普通用户,他们各自拥有不同的权限。在真正开始他们的操作之前,系统要求先登录。

(1)第一次登陆系统后,之后利用Cookie与Session来标识用户。Cookie与Session的区别可以参考这篇文章Cookie与Session的区别

先写好Cookie与Session的工具类备用

CookieUtil类,我这边的Cookie生成规则为时间戳+用户id+用户类型,平台管理员为0,普通用户为1

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 管理cookie
 */
public class CookieUtil {

    public static final String COOKIE_NAME = "code";

    public static String generateCookieByUserId(Integer userId, Integer type) {
        Long currentTime = System.currentTimeMillis();
        return currentTime + "" + userId + "" + type;
    }

    public static String getCookieValueFromRequest(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (null != cookies) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(COOKIE_NAME)) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }

    public static void setCookieValueIntoResponse(HttpServletResponse response, String value) {
        Cookie cookie = new Cookie(COOKIE_NAME, value);
        response.addCookie(cookie);
    }
}

SessionUtil类,服务端内部维护一个map,键为用户id,值为对应的cookie值。每次用户访问时,取出请求的cookie,遍历map,如果与cookie存在对应的键值对,则说明用户已经登陆。

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;


/**
 * 管理Session
 */
public class SessionUtil {

    //K:用户id值 V:对应的cookie值
    private static HashMap map;

    static {
        map = new HashMap<>();
    }

    public static void putSession(Integer key, String value) {
        map.put(key, value);
    }

    public static String getSession(Integer key) {
        return map.get(key);
    }

    public static boolean containsKey(Integer key) {
        return map.containsKey(key);
    }

    public static boolean containsValue(String value) {
        return map.containsValue(value);
    }

    public static Integer getKeyByValue(String value) {
        for (Integer key : map.keySet()) {
            if (map.get(key).equals(value)) {
                return key;
            }
        }
        return -1;
    }

    public static void removeSession(Integer key) {
        map.remove(key);
    }


}

 

(2)定义角色常量类与角色注解@RoleNum

角色常量类

public class Role {

    /**
     * 需要管理员角色
     */
    public static final int ADMIN = 0;

    /**
     * 需要普通用户角色
     */
    public static final int NORMAL = 1;

    /**
     * 拥有管理员或普通用户角色即可
     */
    public static final int COMMON = 2;

}

@RoleNum中定义int型角色变量,0代表需要管理员角色,1代表拥有普通用户角色即可,2代表两种角色即可访问。

自定义注解可以参考这篇文章使用自定义注解简易模拟Spring中的自动装配@Autowired

import java.lang.annotation.*;

/**
 * 0代表需要管理员角色,1代表拥有普通用户角色即可,2代表两种角色即可访问
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RoleNum {
    int value();
}

这个注解加在Controller的类上或方法上,代表访问该类或方法前需要该注解代表的角色。


(3)设置拦截器,拦截前端每次对Controller的请求

package com.paas.boc.license.handler;

import com.paas.boc.license.annotation.RoleNum;
import com.paas.boc.license.util.CookieUtil;
import com.paas.boc.license.util.SessionUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SecurityInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //先查看服务器端是否存在cookie对应的session
        String cookie = CookieUtil.getCookieValueFromRequest(request);
        response.setCharacterEncoding("UTF-8");
        if (!SessionUtil.containsValue(cookie)) {
            response.getWriter().write("{\"code\":-1,\"msg\":\"请先登录\",\"data\":null}");
            return false;
        } else {
            //查看该cookie对应的user_id是否拥有访问该路径的权限
            Integer type = Integer.parseInt(cookie.substring(cookie.length() - 1));
            if (hasPermission(handler, type)) {
                return true;
            } else {
                response.getWriter().write("{\"code\":-1,\"msg\":\"权限不够\",\"data\":null}");
                return false;
            }
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    private boolean hasPermission(Object handler, Integer type) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取方法上的注解
            RoleNum roleNum = handlerMethod.getMethod().getAnnotation(RoleNum.class);
            // 如果方法上的注解为空 则获取类的注解
            if (roleNum == null) {
                roleNum = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RoleNum.class);
            }
            // 如果标记了注解,则判断权限
            if (roleNum != null) {
                if (roleNum.value() == 2) {
                    return true;
                }
                if (roleNum.value() == type) {
                    return true;
                }
                return false;
            }
        }
        return false;
    }
}


(4)注入拦截器的配置类

excludePathPatterns代表排除对该文件或方法拦截

import com.paas.boc.license.handler.SecurityInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SecurityConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/*.js")
                .excludePathPatterns("/*.css")
                .excludePathPatterns("/**.html")
                .excludePathPatterns("/user/login");
    }
}

(5)应用到Controller中

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.paas.boc.license.annotation.RoleNum;
import com.paas.boc.license.constant.Role;
import com.paas.boc.license.entity.UserInfo;
import com.paas.boc.license.plugins.UserCreatorParam;
import com.paas.boc.license.service.UserInfoService;
import com.paas.boc.license.util.AESUtil;
import com.paas.boc.license.util.CookieUtil;
import com.paas.boc.license.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    UserInfoService userInfoService;

    //用户登录
    @RoleNum(Role.COMMON)
    @PostMapping("/login")
    public R login(HttpServletResponse response, String username, String password) {
        return userInfoService.login(response, username, password);
    }

    //用户注销
    @RoleNum(Role.COMMON)
    @GetMapping("/logout")
    public R logout(@CookieValue(CookieUtil.COOKIE_NAME) String code) {
        return userInfoService.logout(code);
    }


    //管理员查看所有用户
    @RoleNum(Role.ADMIN)
    @GetMapping("/page")
    public R> page(Page page) {
        return userInfoService.page(page);
    }

    //管理员创建普通用户
    @RoleNum(Role.ADMIN)
    @PostMapping("/create")
    public R create(@Valid UserCreatorParam userCreatorParam) {
        return userInfoService.create(userCreatorParam);
    }

    //管理员或普通用户修改自己的密码
    @RoleNum(Role.COMMON)
    @PostMapping("/updatePassword")
    public R updatePassword(@CookieValue(CookieUtil.COOKIE_NAME) String code, String password) {
        return userInfoService.updatePassword(code, password);
    }

}
 
  

 

你可能感兴趣的:(SpringBoot实践)