shiro jwt 实现权限控制


一.导入shiro和jwt的包

<jwt.version>3.4.0</jwt.version>


<shiro.version>1.3.2</shiro.version>


<!-- JWT -->  
<dependency>  
   <groupId>com.auth0</groupId>  
   <artifactId>java-jwt</artifactId>  
   <version>${jwt.version}</version>  
</dependency>  
<!-- Shiro -->  
<dependency>  
   <groupId>org.apache.shiro</groupId>  
   <artifactId>shiro-spring</artifactId>  
   <version>${shiro.version}</version>  
</dependency>  

二.数据库表结构  
 **1.用户表:sys_amdin**

2.角色表:sys_role

3.用户角色中间表:sys_amdin_role

4.权限表:sys_permission

5. 角色权限中间表:sys_role_permission

三.jwt工具类(token加密解密)JwtUtilAdmin

在配置文件里定义三个变量

accessTokenExpireTime=2592000  
refreshTokenExpireTime=31104000  
shiroCacheExpireTime=31104000  



@Component
public class JwtUtilAdmin {

    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtilAdmin.class);
    /**
     * 过期时间改为从配置文件获取
     */
    private static String accessTokenExpireTime;

    private static Integer shiroCacheExpireTime;
    /**
     * JWT认证加密私钥(Base64加密)
     */
    private static String encryptJWTKey;

    @Value("${accessTokenExpireTime}")
    public void setAccessTokenExpireTime(String accessTokenExpireTime) {
        JwtUtilAdmin.accessTokenExpireTime = accessTokenExpireTime;
    }

    @Value("${shiroCacheExpireTime}")
    public void setShiroCacheExpireTime(Integer shiroCacheExpireTime) {
        JwtUtilAdmin.shiroCacheExpireTime = shiroCacheExpireTime;
    }
    @Value("${encryptJWTKey}")
    public void setEncryptJWTKey(String encryptJWTKey) {
        JwtUtilAdmin.encryptJWTKey = encryptJWTKey;
    }
    /**
     * 校验token是否正确
     * 
     * @param token
     *            Token
     * @return boolean 是否正确
     */
    public static boolean verify(String token) {
        try {
            // 帐号加JWT私钥解密
            String secret = getClaim(token, Constant.ACCOUNT) +  Base64.decodeStr(encryptJWTKey, "utf-8");
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)./*acceptExpiresAt(20).*/build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (JWTDecodeException e) {
            LOGGER.error("JWTToken认证解密出现UnsupportedEncodingException异常:" + e.getMessage());
            throw new CustomException("JWTToken认证解密出现UnsupportedEncodingException异常:" + e.getMessage());
        }
    }
    /**
     * 获得Token中的信息无需secret解密也能获得
     * 
     * @param token
     * @param claim
     * @return java.lang.String
     */
    public static String getClaim(String token, String claim) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            // 只能输出String类型,如果是其他类型返回null
            return jwt.getClaim(claim).asString();
        } catch (JWTDecodeException e) {
            LOGGER.error("解密Token中的公共信息出现JWTDecodeException异常:" + e.getMessage());
            throw new CustomException("解密Token中的公共信息出现JWTDecodeException异常:" + e.getMessage());
        }
    }
    /**
     * 生成签名
     * 
     * @param account
     *            帐号
     * @return java.lang.String 返回加密的Token
     * @throws  
     */
    public static String sign(String account, String currentTimeMillis)  {
        try {
            // 帐号加JWT私钥加密
            String secret = account + Base64.decodeStr(encryptJWTKey, "utf-8");
            // 此处过期时间是以毫秒为单位,所以乘以1000
            Date date = new Date(System.currentTimeMillis() + Long.parseLong(accessTokenExpireTime) * 1000);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带account帐号信息
            return JWT.create().withClaim(Constant.ACCOUNT, account).withClaim(Constant.CURRENT_TIME_MILLIS, currentTimeMillis)
                    .withExpiresAt(date).sign(algorithm);
        } catch (JWTCreationException e) {
            LOGGER.error("JWTToken加密出现IllegalArgumentException异常:" + e.getMessage());
            throw new CustomException("JWTToken加密出现IllegalArgumentException异常:" + e.getMessage());
        }
    }

    /**
     * 生成签名(业务员,门店账号)
     * @param account  帐号
     * @return java.lang.String 返回加密的Token
     */
    public static String signOther(String account, String currentTimeMillis) {
        try {
            // 帐号加JWT私钥加密
            String secret = account + Base64.decode(encryptJWTKey);
            // 此处过期时间是以毫秒为单位,所以乘以1000
            Date date = new Date(System.currentTimeMillis() + shiroCacheExpireTime *24*60*60*1000l);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带account帐号信息
            return JWT.create().withClaim(Constant.ACCOUNT, account).withClaim(Constant.CURRENT_TIME_MILLIS/*"currentTimeMillis"*/, currentTimeMillis)
                    .withExpiresAt(date).sign(algorithm);
        } catch (JWTDecodeException e) {
            LOGGER.error("JWTToken加密出现UnsupportedEncodingException异常:" + e.getMessage());
            throw new CustomException("JWTToken加密出现UnsupportedEncodingException异常:" + e.getMessage());
        }
    }
}

四.账号密码登录实现类

@Override
    public BaseResponse<?> loginPassword(String account, String password) {
        //交于自定义的UserRealm校验
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(account, password, password);
        SecurityUtils.getSubject().login(usernamePasswordToken);
        //登录成功返回vo
        SysAdmin admin = new SysAdmin();
        admin.setAccount(account);
        admin.setState(AdminStateEnum.USABLE.getCode());
        List<SysAdmin> admins = this.baseMapper.select(admin);
        admin = admins.get(0);
        AdminLoginVo adminLoginVo = getAdminLoginVo(admin);
        adminLoginVo.setAdminId(admin.getId());
        if( adminLoginVo.getClinicId() == null){
            return BaseResponse.error("诊所信息有误");
        }
        if(!RoleStateEnum.USABLE.getCode().equals(adminLoginVo.getRoleState()) || adminLoginVo.getRoleId() == null){
            return BaseResponse.error("角色未启用");
        }
        //由于http的session不便于测试故舍弃,用security的session
        Session session = SecurityUtils.getSubject().getSession();
        loginSuccess(session, admin.getId(), adminLoginVo);
        return BaseResponse.success(adminLoginVo);
    }
    //登录成功,将用户信息存入session,redis并返回
    private void loginSuccess(Session session, Integer adminId, AdminLoginVo adminLoginVo){
        //登录用户ID加密得到token
        String token = JwtUtilAdmin.sign(adminId.toString(), System.currentTimeMillis() + "");
        //登录成功返回token
        adminLoginVo.setToken(token);
        //永久保存
        session.setTimeout(-1);
        session.setAttribute(Constant.CURRENT_USER, adminLoginVo);
        //以ID为key, 当前时间为value存进redis
        iRedisService.set(KeyUtil.LOGIN_KEY + token, adminLoginVo, 24 * 60 * 60 * 30 * 36);
    }
    //得到登录返回的AdminLoginVo
    private AdminLoginVo getAdminLoginVo(SysAdmin admin){
        Clinic clinic = iClinicService.selectById(admin.getClinicId());
        SysRole sysRole = iSysRoleService.selectByAdminId(admin.getId());
        AdminLoginVo adminLoginVo = new AdminLoginVo(admin.getState(), admin.getAuthState());
        adminLoginVo.setClinicId(admin.getClinicId());
        adminLoginVo.setRoleType(admin.getClinicId());
        adminLoginVo.setRoleTypeName(RoleTypeNameUtil.getRoleTypeName(adminLoginVo.getRoleType()
                , adminLoginVo.getRoleTypeName()));
        if(clinic != null){
            adminLoginVo.setClinicName(clinic.getClinicName());
            adminLoginVo.setClinicId(clinic.getId());
        }
        if(sysRole != null){
            adminLoginVo.setRoleName(sysRole.getRoleName());
            adminLoginVo.setRoleId(sysRole.getId());
            adminLoginVo.setRoleState(sysRole.getState());
        }
        return adminLoginVo;
    }

五.自定义shiroConfig

首先创建JwtFilterAdmin过滤

public class JwtFilterAdmin extends BasicHttpAuthenticationFilter {
    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtFilterAdmin.class);

    private IRedisService redisService;
    {

    }

    /**
     * 这里我们详细说明下为什么最终返回的都是true,即允许访问
     * 例如我们提供一个地址 GET /article
     * 登入用户和游客看到的内容是不同的
     * 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西
     * 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
     * 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可
     * 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        // 查看当前Header中是否携带Authorization属性(Token),有的话就进行登录认证授权
        if (this.isLoginAttempt(request, response)) {
            try {
                // 进行Shiro的登录UserRealm
                this.executeLogin(request, response);
            } catch (Exception e) {
                // 认证出现异常,传递错误信息msg
                String msg = e.getMessage();
                // 获取应用异常(该Cause是导致抛出此throwable(异常)的throwable(异常))
                Throwable throwable = e.getCause();
                if (throwable instanceof SignatureVerificationException) {
                    // 该异常为JWT的AccessToken认证失败(Token或者密钥不正确)
                    msg = "Token或者密钥不正确(" + throwable.getMessage() + ")";
                } else if (throwable instanceof TokenExpiredException) {
                    // 该异常为JWT的AccessToken已过期,判断RefreshToken未过期就进行AccessToken刷新
                    try{
                        if (this.refreshToken(request, response)) {
                            return true;
                        } else {
                            msg = "Token已过期(" + throwable.getMessage() + ")";
                        }
                    }catch(Exception ex){
                         msg = "Token已过期(" + ex + ")";
                    }
                } else {
                    // 应用异常不为空
                    if (throwable != null) {
                        // 获取应用异常msg
                        msg = throwable.getMessage();
                    }
                }
                /*
                  错误两种处理方式
                  1. 将非法请求转发到/401的Controller处理,抛出自定义无权访问异常被全局捕捉再返回Response信息
                  2. 无需转发,直接返回Response信息
                  一般使用第二种(更方便)
                 */
                // 直接返回Response信息
                this.response401(response, msg);
                return false;
            }
        } else {
            // 没有携带Token
            HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
            // 获取当前请求类型
            String httpMethod = httpServletRequest.getMethod();
            // 获取当前请求URI
            String requestURI = httpServletRequest.getRequestURI();
            LOGGER.info("当前请求 {} Authorization属性(Token)为空 请求类型 {}", requestURI, httpMethod);
            // mustLoginFlag = true 开启任何请求必须登录才可访问
            Boolean mustLoginFlag = false;  //开启h5页面不能访问?
            if (mustLoginFlag) {
                this.response401(response, "请先登录");
                return false;
            }
        }
        return true;
    }

    /**
     * 这里我们详细说明下为什么重写
     * 可以对比父类方法,只是将executeLogin方法调用去除了
     * 如果没有去除将会循环调用doGetAuthenticationInfo方法
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        this.sendChallenge(request, response);
        return false;
    }

    /**
     * 检测Header里面是否包含Authorization字段,有就进行Token登录认证授权
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        // 拿到当前Header中Authorization的AccessToken(Shiro中getAuthzHeader方法已经实现)
        //String token = this.getAuthzHeader(request);
        String token = WebUtils.toHttp(request).getHeader("token");
        return StringUtils.isNotBlank(token);
    }

    /**
     * 进行AccessToken登录认证授权
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        String tokenStr = WebUtils.toHttp(request).getHeader("token");
        LOGGER.info("获得header里的token= {}", tokenStr);
        JwtTokenAdmin token = new JwtTokenAdmin(tokenStr);
        // 提交给UserRealm进行认证,如果错误他会抛出异常并被捕获
        this.getSubject(request, response).login(token);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 此处为AccessToken刷新,进行判断RefreshToken是否过期,未过期就返回新的AccessToken且继续正常访问
     */
    private boolean refreshToken(ServletRequest request, ServletResponse response) {
        String token = WebUtils.toHttp(request).getHeader("token");
        // 获取当前Token的帐号信息
        String account = JwtUtilAdmin.getClaim(token, Constant.ACCOUNT);

        Map<String, String> oldTokenMap = redisService.hmget(Constant.PREFIX_SHIRO_REFRESH_TOKEN + account);
        String refreshExpireTime = oldTokenMap.get(token);
        if (refreshExpireTime != null) {
            String key = KeyUtil.keyOf(KeyUtil.TRY_LOCK, account);
            try{
                if(redisService.tryLock(key)){  //控制异步并发请求
                    String newToken = refreshToken(account);
                    if(newToken == null) {
                        return false;
                    }
                    JwtTokenAdmin jwtTokenAdmin = new JwtTokenAdmin(newToken);
                    this.getSubject(request, response).login(jwtTokenAdmin);
                    LOGGER.info("旧的token={}被新token{}替换", token, newToken);
                    return true;
                }
            }finally{
                redisService.remove(key);
            }
        }
        return true;
    }


    /**
     * token 过期 才能用
     * @param id
     */
    public String refreshToken(String id){
        //过期token
        String old = Constant.PREFIX_SHIRO_OLD_TOKEN + id;
        //当前token
        String current = Constant.PREFIX_SHIRO_REFRESH_TOKEN + id;
        Map<String, String> map = null;
        if(redisService.exsits(old)) {    //当前token已过期,并且token已刷新, 返回刷新过的新token
            map = redisService.hmget(Constant.PREFIX_SHIRO_REFRESH_TOKEN + id);
            for(String token : map.keySet()) {
                return token;
            }
            return null;
        }else {
            //刷新token
            String currentTimeMillis = String.valueOf(System.currentTimeMillis());
            String token = JwtUtilAdmin.sign(id, currentTimeMillis);   //accessExpireTime
            try{
                map = redisService.hmget(Constant.PREFIX_SHIRO_REFRESH_TOKEN + id);
                map.put(token, currentTimeMillis);
            }catch(Exception e){
                map = Maps.newHashMap();
                map.put(token, currentTimeMillis);
            }
            //30天 超时时间
            String refreshTokenExpireTime = SpringContextUtil.getEnvironmentProperty("refreshTokenExpireTime");
            redisService.hmset(Constant.PREFIX_SHIRO_REFRESH_TOKEN + id, map, Integer.parseInt(refreshTokenExpireTime));
            return token;
        }
    }
    /**
     * 无需转发,直接返回Response信息
     */
    private void response401(ServletResponse response, String msg) {
        LOGGER.info("3" + Thread.currentThread().getName());
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setStatus(HttpStatus.OK.value());
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = httpServletResponse.getWriter()) {
            Map<Integer, String> result = Maps.newHashMap();
            result.put(401, "无权访问,请重新登录");
            String data = JSON.toJSONString(result);
            out.append(data);
        } catch (IOException e) {
            LOGGER.error("直接返回Response信息出现IOException异常:" + e.getMessage());
            throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
        }
    }

    private void setResponseheader(String token, ServletResponse response){
        // 最后将刷新的AccessToken存放在Response的Header中的Authorization字段返回
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Authorization", token);
        httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
    }
}

在shiroConfig里调用我们创建的jwt拦截规则

@Configuration
public class ShiroConfig {

    /**
     * 配置使用自定义Realm,关闭Shiro自带的session 详情见文档
     * http://shiro.apache.org/session-management.html#SessionManagement-
     * StatelessApplications%28Sessionless%29
     *
     * @param userRealm
     */
    @Bean("securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(MyRealmAdmin userRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 使用自定义Realm
        defaultWebSecurityManager.setRealm(userRealm);
        // 关闭Shiro自带的session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        defaultWebSecurityManager.setSubjectDAO(subjectDAO);
        // 设置自定义Cache缓存
        defaultWebSecurityManager.setCacheManager(new CustomCacheManager());
        return defaultWebSecurityManager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 添加自己的过滤器取名为jwt
        Map<String, Filter> filterMap = new HashMap<>(16);
        filterMap.put("jwt", new JwtFilterAdmin());
        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);
        /**
         * anon:无需认证
         * authc:必须认证
         * user:如果使用rememberMe可直接访问
         * perms:该资源必须得到资源权限才可以访问
         * role:该资源必须得到资源权限才可以访问
         */
        // 自定义url规则使用LinkedHashMap有序Map
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(16);
        //登录
        filterChainDefinitionMap.put("/login/*", "anon");
        //获取验证码
        filterChainDefinitionMap.put("/phone/code", "anon");
        //获取角色类型
        filterChainDefinitionMap.put("/role/type", "anon");
        //无权限异常
        filterChainDefinitionMap.put("/exception/getException", "anon");
        //上传图片/文件
        filterChainDefinitionMap.put("/img/getUrl", "anon");
        //jwt拦截
        filterChainDefinitionMap.put("/**", "jwt");
        factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return factoryBean;
    }
    /**
     * 下面的代码是添加注解支持
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题,https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }  

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }  

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}


六.自定义userRealm


public abstract class MyRealmAdmin extends AuthorizingRealm {


}


@Service
public class UserRealm extends MyRealmAdmin {

    @Autowired
    private IRedisService iRedisService;

    @Autowired
    private ISysAdminService iSysAdminService;
    @Autowired
    private ISysRoleService iSysRoleService;
    @Autowired
    private ISysPermissionService iSysPermissionService;

    @Autowired
    private IClinicService iClinicService;

    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof JwtTokenAdmin || authenticationToken instanceof UsernamePasswordToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        String account = JwtUtilAdmin.getClaim(principalCollection.toString(), Constant.ACCOUNT);
        SysAdmin userDto = new SysAdmin();
        userDto.setId(Integer.valueOf(account));
        // 查询用户角色
        SysRole roleDto = iSysRoleService.selectByAdminId(Integer.valueOf(account));
        if (roleDto != null) {
            // 根据用户角色查询权限
            List<SysPermission> permissionDtos = iSysPermissionService.selectByRoleId(roleDto.getId());
            for (SysPermission permissionDto : permissionDtos) {
                if (permissionDto != null && StringUtils.isNotBlank(permissionDto.getPermissionStr())) {
                    // 添加权限
                    simpleAuthorizationInfo.addStringPermission(permissionDto.getPermissionStr());
                }
            }
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)  throws AuthenticationException {
        if(authenticationToken instanceof UsernamePasswordToken){
            UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
            String account = token.getUsername();
            String password = token.getHost();
            //账号密码登录校验
            SysAdmin admin = new SysAdmin();
            admin.setAccount(account);
            admin.setState(AdminStateEnum.USABLE.getCode());
            List<SysAdmin> admins = iSysAdminService.select(admin);
            if(admins.size() == 0){
                throw new AuthenticationException("账号不存在");
            }
            if(admins.size() > 1){
                throw new AuthenticationException("账号信息有误");
            }
            admin = admins.get(0);
            if(!admin.getPassword().equals(MD5Util.MD5SaltEncodeing(password))){
                throw new AuthenticationException("密码错误,请重新填写");
            }
            if(!AdminAuthStateEnum.PASS.getCode().equals(admin.getAuthState())){
                throw new AuthenticationException(AdminAuthStateEnum.valueOfCode(admin.getAuthState()).getMessage());
            }
            return new SimpleAuthenticationInfo(token, password, account);
        }else {
            //调用其它接口时token校验
            Object credential = authenticationToken.getCredentials();
            if(credential == null){
                throw new AuthenticationException("Token为空(The Token is empty.)");
            }
            String token = credential.toString();

            // 解密获得account,用于和数据库进行对比
            if (JwtUtilAdmin.verify(token)) {
                String account = JwtUtilAdmin.getClaim(token, Constant.ACCOUNT);
                if (StringUtil.isBlank(account)) {
                    throw new AuthenticationException("Token中帐号为空(The account in Token is empty.)");
                }
                AdminLoginVo adminLoginVo = iRedisService.get(KeyUtil.LOGIN_KEY + token, AdminLoginVo.class);
                if (adminLoginVo != null) {
                    SysAdmin sysAdmin = iSysAdminService.selectById(Integer.valueOf(account));
                    if (sysAdmin == null) {
                        throw new AuthenticationException("该帐号不存在(The account does not exist.)");
                    }
                    if(!RoleTypeEnum.USABLE.getCode().equals(adminLoginVo.getRoleType())){
                        Clinic  clinic =  iClinicService.selectById(sysAdmin.getClinicId());
                        if(!Constant.ZERO.equals(clinic.getState())) {
                            switch (clinic.getState()) {
                                case 10:   throw new AuthenticationException("该诊所已被禁用");
                                case 20:   throw new AuthenticationException("该诊所正在审核中");
                                case 30:   throw new AuthenticationException("该诊所审核失败");
                                default:   break;
                            }
                        }
                    }
                    return new SimpleAuthenticationInfo(token, token, account);
                }
            }
            throw new AuthenticationException("Token已过期(Token expired or incorrect.)");
        }
    }
}

七.JwtTokenAdmin

public class JwtTokenAdmin implements AuthenticationToken {
    private static final long serialVersionUID = 960867003694917627L;
    /**
     * Token
     */
    private String token;

    public JwtTokenAdmin(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}


八.关于授权  
 **在接口上加入注解, 引号内为菜单标识, 需存入权限表sys_permission 的permission_str字段里** 


@RequiresPermissions("super:admin:list")

sys_permission:

sys_role_permission:

sys_role:

sys_admin_role:

sys_admin:

接口代码:


   /**
    * @description: //:超级管理员列表
    * @auth onfec
    * @param keyWord 根据账号, 姓名, 手机号模糊查询
    * @return com.sysf.doc.BaseResponse<?>
    * @date 2020/7/7 11:48
    */
   @RequiresPermissions("super:admin:list")
    @RequestMapping("list")
    public BaseResponse<?> superList(String keyWord, Integer state, String startTime, String endTime,
                                     @RequestParam(value = "pageNum", defaultValue = "0") Integer pageNum,
                                     @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize){
        keyWord = NullUtil.getStringNull(keyWord);
        state = NullUtil.getIntegerNull(state);
        startTime = NullUtil.getStringNull(startTime);
        endTime = NullUtil.getStringNull(endTime);
        return iSysAdminService.superList(keyWord, state, startTime, endTime, pageNum, pageSize);
    }


这样我们的用户: admin 就可以访问 超级管理员列表 这个接口了


原文链接:https://www.cnblogs.com/ONFE/p/13447433.html