Shiro+JWT


关于两大认证鉴权框架,springsecurity,shiro,确实瞎看些文章,眼花缭乱,用了一周的时间差不多了解了,学习过程中可谓晕头转向的,后面在谛听上看到shiro文章很好,又得到行内大佬指点一下(鉴权信息来源于认证),算是弄懂了shiro,然后也对springsecurity有了基本了解

继承BasicHttpAuthenticationFilter实现自定义JWTFilter

package com.fintech.abchina.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

public class MyJWTFilter extends BasicHttpAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        System.err.println("isAccessAllowed");

        HttpServletRequest request1 = (HttpServletRequest) request;
        String requestURI = request1.getRequestURI();
        System.err.println("requestURI = " + requestURI);
        if (requestURI.contains("login") || requestURI.contains("/js")) {
        //这里为了测试使用,写的不是很标准,作用就是释放登录和静态资源请求
            return true;
        } else {
            if (isLoginAttempt(request, response)) {
                try {
                    this.executeLogin(request, response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }


    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        System.err.println("executeLogin");
        HttpServletRequest request1 = (HttpServletRequest) request;

        String token1 = request1.getHeader("token");;
        System.err.println("token1 = " + token1);

        JwtToken jwtToken = new JwtToken(token1);
        System.err.println("jwtToken = " + jwtToken);

        try {
            this.getSubject(request, response).login(jwtToken);
            //这里调用的其实就是ShiroRealm中的doAuthentication方法
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return true;
    }

	//判断是否携带token,携带token则进行后续认证
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        System.err.println("isLoginAttempt");
        //顾名思义,这方法名不是代表登录吗,而现在后续认证,应该非首次登录
   
        HttpServletRequest request1 = (HttpServletRequest) request;
        String token1 = request1.getHeader("token");
        return token1 != null;
    }
}

继承AuthorizingRealm实现自己的Realm

package com.fintech.abchina.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author: GQS
 * @Description:
 * @Date: 2021/8/12 17:18
 */
public class ShiroRealm extends AuthorizingRealm {

    //将shiro默认token修改为自定义的token
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    //授权(即用户访问有要求权限接口时获取要求权限,再在这里获取该角色具有哪些权限,由shiro进行比较 )
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //这个principalCollection是shiro根据认证方法返回的对象第一个参数提供的。
 		System.err.println("doGetAuthorizationInfo");
        System.out.println("principalCollection = " + principalCollection);
        System.out.println("看一下PrincipalCollection 使用的哪个实现类 " + principalCollection.getClass());
        
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        
        Set<String> roleSet = new HashSet<>();
        //实际这个是要根据principalCollection来查的
        roleSet.add("admin");
        simpleAuthorizationInfo.setRoles(roleSet);
        
        return simpleAuthorizationInfo;
        //返回权限后,就由shiro来进行用户该访问接口的权限判断啦。
        //应该是每次都要进行判断的,因为每次请求都要走到这里,多写一个ajax按钮来判断一下
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        System.err.println("doGetAuthenticationInfo");

        String token = "";
        try {
            token = (String) authenticationToken.getPrincipal();
        } catch (Exception e) {
            System.out.println("e.getMessage() = " + e.getMessage());
        }
        System.out.println("对token进行判断过期,是否为空等");
		//根据传进来token
        String username= JwtUtil.getUserNameByToken(token);
        System.out.println("根据username查询数据库User信息,再使用当初返回该token相同方式生成token1");
        String token_right= username+"";
        System.out.println("这里为了测试,直接省略具体生成方法");

        //第一个参数可以返回用户名,认证通过后用于鉴权。
        //根据前台提供的token信息按照规则(利用真是比如密码)生成一个新的token_right,由shiro进行两者的比较
        //即authenticationToken.getCredentials和token1进行比较。可以打断点进去查看的
        return new SimpleAuthenticationInfo("principal", token_right, "my_shiro_realm");
    }
}

实现AuthenticationToken接口,实现自定义的token

需要在自定义Realm这里叫ShiroRealm进行配置

package com.fintech.abchina.config;

import lombok.Data;
import org.apache.shiro.authc.AuthenticationToken;
import java.io.Serializable;

@Data
public class JwtToken implements AuthenticationToken {

    private String token;
    private String expireAt;

    public JwtToken(String token) {
        this.token = token;
        this.expireAt="time";
    }

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

配置类配置,将类进行绑定

package com.fintech.abchina.config;

import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextListener;
import javax.servlet.Filter;
import java.util.LinkedHashMap;

@Configuration
public class ShiroConfig {

    //这里测一下是否一定要将MyJwtFilter加入到shiro过滤器链中  -是的,不加不执行
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //在shiro过滤器链上加入MyJwtFilter,不加入不会执行。
        LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
        filters.put("jwt", new MyJWTFilter());
        shiroFilterFactoryBean.setFilters(filters);

        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //过滤器链定义,由上到下执行
        filterChainDefinitionMap.put("/**", "jwt");
        //这个和前面对应
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getShiroRealm());
        return securityManager;
    }

    //将自定义Realm加入到Spring环境中
    @Bean
    public ShiroRealm getShiroRealm() {
        return new ShiroRealm();
    }

    //用于鉴权时候使用的
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

	//配置鉴权生效的(具体不清楚)
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

	//暂时不清楚
    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }
}

异常处理

有个很重要思想就是shiro内部异常由shiro拦截,我们只能捕获最上层异常

package com.fintech.abchina.config;

import org.apache.shiro.authc.CredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class ControllerAdvice {
    
    @ExceptionHandler(ShiroException.class)
    Map handleShiroException(ShiroException e) {
        System.err.println("e.getClass() = " + e.getClass());
        HashMap<String, String> resultMap = new HashMap<>();
        if (e instanceof UnauthenticatedException) {
            System.out.println("认证异常");
            resultMap.put("msg", "认证异常");
        } else if (e instanceof UnauthorizedException) {
            System.out.println("权限异常");
            resultMap.put("msg", "权限异常");
        } else {
            System.out.println("其他异常");
            resultMap.put("msg", "其他异常");
        }
        return resultMap;
    }

}

很重要的思想

1.通过了自定义jwtfilter的isAccessAllowed返回true即登录和静态资源无需后面认证
2.Realm是由自定义的jwtfilter调用
3.授权和认证都有shiro自己完成,我们在认证和授权里需要做的就是返回信息
4.授权的用户信息通过认证返回的类获得,即先认证后授权

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值