springboot shiro jwt整合

提示:本配置的预期读者是熟悉shiro的session配置的开发者.如果不熟悉我有其他博客关于session式,可以查看

shiro默认是以session来确权的

现在以jwt的方式使用,那么登录方法就不再subject.login,而是返回jwt字符串,我们将userid放进去.

    @GetMapping("login")
    public String login(String username,String password) throws IllegalArgumentException, JWTCreationException, UnsupportedEncodingException{
    
    
    	
		//数据库查询
		User user = login(username,password)....
		
		//把userid附上,生成jwt返回给前台
		String sign = JWT.create().withClaim("userid", user.getId())
			.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*24*3))
			.sign(Algorithm.HMAC256("123456"));
		
        return sign;
    }

jwt的话需要做一个jwtfilter,进行解析jwt字串.然后登录

这里继承自AccessControlFilter, 核心方法在onAccessDenied里面.

@Slf4j
public class JwtFilter extends AccessControlFilter {
    
    
    /*
     * 1. 返回true,shiro就直接允许访问url
     * 2. 返回false,shiro才会根据onAccessDenied的方法的返回值决定是否允许访问url
     * */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    
    
        return false;
    }

    /**
     * 返回结果为true表明登录通过
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
    
    
        log.warn("onAccessDenied 方法被调用");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        
        String token = request.getHeader("token");
		if (StringUtils.isBlank(token)){
    
    
			request.getRequestDispatcher("/error").forward(request, response);//如果没有token跳转到错误提示
			return false;
		}
		try {
    
    
			JWT.require(Algorithm.HMAC256("123456")).build().verify(token);
		} catch (Exception e) {
    
    
			request.getRequestDispatcher("/error").forward(request, response);//如果token校验错误跳转到错误提示
			return false;
		}
		DecodedJWT decode = JWT.decode(token);
		Integer userid = decode.getClaim("userid").asInt();//获取userid
		
		//构造出一个jwtToken
		JwtToken jwtToken = new JwtToken(userid, token);
       
		getSubject(servletRequest, servletResponse).login(jwtToken);//登录
        
        return true;
    }

}

不再使用usernamepasswordtoken,而是新建了一个jwttoken


public class JwtToken implements AuthenticationToken{
    
    
	private Integer id;
	private String token;
	public JwtToken() {
    
    
		super();		
	}
	
	public JwtToken(Integer id, String token) {
    
    
		super();
		this.id = id;
		this.token = token;
	}
	@Override
	public Object getPrincipal() {
    
    
		return id;
	}

	@Override
	public Object getCredentials() {
    
    
		return token;
	}
    ...getter/setter
	
}

了解shiro的都知道核心realm.其实realm都差不多

@Slf4j
public class CustomRealm extends AuthorizingRealm {
    
    

   
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.....set一堆权限
        return info;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
    
    	
        //获取用户信息
        Integer userid = (Integer)authenticationToken.getPrincipal();
        
        log.info("认证.....{}",userid);
        
        User user = getById(userid)....
       
        if (user == null){
    
    
            return null;
        }
        return new SimpleAuthenticationInfo(userid,authenticationToken.getCredentials(),getName());

    }

    //如果是自定义token,则需要重写支持方法
	@Override
	public boolean supports(AuthenticationToken token) {
    
    
		if (token instanceof JwtToken)
			return true;
		return false;
	}
    
}

注意一些配置,这里禁用了session

@Component
public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {
    
    

    @Override
    public Subject createSubject(SubjectContext context) {
    
    
        // 不创建 session
        context.setSessionCreationEnabled(false);
        return super.createSubject(context);
    }
}

虽然有禁用session,但是如果不配置下面的,依然会检查session,但是由于禁用了session,所以会报错.

@Component
public class NoSessionSubjectDAO extends DefaultSubjectDAO{
    
    

	@Override
	protected boolean isSessionStorageEnabled(Subject subject) {
    
    
		// TODO Auto-generated method stub
		return false;
	}
	
}

shiro的核心配置

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.realm.Realm;
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;

/**
 * Shiro的核心配置类,用来整合shiro框架
 */
@Configuration
public class ShiroConfiguration {
    
    

    //1.创建shiroFilter  //负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
    
    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //给filter设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put("jwt", new JwtFilter());//spring控制
		shiroFilterFactoryBean.setFilters(filters );

		//配置资源
        Map<String,String> map = new LinkedHashMap<String,String>();//为什么是LinkedHashMap,因为有序sort
        map.put("/login","anon");
        map.put("/**", "jwt");
        //默认认证界面路径
        shiroFilterFactoryBean.setLoginUrl("/error-login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/error-author");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        
        return shiroFilterFactoryBean;
    }

    //2.创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm,JwtDefaultSubjectFactory jwtDefaultSubjectFactory,NoSessionSubjectDAO noSessionSubjectDAO){
    
    
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //给安全管理器设置
        defaultWebSecurityManager.setRealm(realm);
        defaultWebSecurityManager.setSubjectFactory(jwtDefaultSubjectFactory);//禁用session
		defaultWebSecurityManager.setSubjectDAO(noSessionSubjectDAO );//不再保存session
        return defaultWebSecurityManager;
    }

    //3.创建自定义realm
    @Bean
    public Realm getRealm(){
    
    
        CustomRealm customerRealm = new CustomRealm();
        return customerRealm;
    }
   

    /**
     * 配置注解方式权限
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
    
    
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
                = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 配置注解方式权限
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    
    
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }
    
}

结语:

能完美使用token

猜你喜欢

转载自blog.csdn.net/dmw412724/article/details/120420240