springboot-shiro-jwt 整合

之前我有整合springboot-jwt这种(参见https://blog.csdn.net/qq_14926283/article/details/110653829) 今天就在它这基础上来整合shiro  

Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro 要简单的多。

Apache Shiro 是 Java 的一个安全框架,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是实际用到的我们没有这么复杂的那么多的东西,所以我们用到shiro就比较多。

Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。

接下来我们分别从外部和内部来看看 Shiro 的架构,对于一个好的框架,从外部来看应该具有非常简单易于使用的 API,且 API 契约明确;从内部来看的话,其应该有一个可扩展的架构,即非常容易插入用户自定义实现,因为任何框架都不能满足所有需求。

 

好了我们之间以最简单的形式来集成一下

由于是基于之前的demo改的,我这里就只把关键的代码贴出来

    <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.3.2</version>
        </dependency>

我这边只多引入了这一个jar坐标

package com.zkb.filter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.zkb.shiro.JwtToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public class JwtFilter extends BasicHttpAuthenticationFilter{

	private Logger logger = LoggerFactory.getLogger(JwtFilter.class);

    /**
     *  校验token
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("token");
        
        logger.info("isToken: "+authorization);

        if(authorization != null){
            Subject subject = getSubject(request, response);
            subject.login(new JwtToken(authorization));
            return true;
        }

        return false;
    }
}
package com.zkb.shiro;

import org.apache.shiro.authc.AuthenticationToken;


public class JwtToken implements AuthenticationToken {

	private static final long serialVersionUID = 1L;
    private String token;

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

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

    @Override
    public Object getCredentials() {
        return token;
    }
}
package com.zkb.shiro;

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

import javax.servlet.Filter;

import com.zkb.filter.JwtFilter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;

@Configuration
public class ShiroConfig {
	
    //管理shiro一些bean的生命周期,生命周期就是初始化与销毁的管理
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    
    //自动创建代理类
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }
    
    //自定义realm
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRealm shiroRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        return shiroRealm;
    }

	//会话管理器
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        
        //获取securityManager的SubjectDao的实现类
        DefaultSubjectDAO subjectDAO = (DefaultSubjectDAO)securityManager.getSubjectDAO();
        //获取subjectDao的SessionStorageEvaluator的实现类
        DefaultSessionStorageEvaluator sessionStorageEvaluator = (DefaultSessionStorageEvaluator)subjectDAO.getSessionStorageEvaluator();
        //禁用session的存储策略
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        	
        securityManager.setRealm(this.shiroRealm());
        
        return securityManager;
    }
    
    //会话管理类 禁用session
    @Bean
    public DefaultSessionManager defaultSessionManager(){
         DefaultSessionManager manager = new DefaultSessionManager();
         manager.setSessionValidationSchedulerEnabled(false);
         return manager;
    }
    
    //不注入此实例,shiro注解将不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }
    
    //shiro过滤器
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/unauthenticated");
        shiroFilter.setUnauthorizedUrl("/unauthorized");

		Map<String, Filter> filterMap = new HashMap<>();
	    filterMap.put("jwt", new JwtFilter());
	    shiroFilter.setFilters(filterMap);


	    Map<String, String> filterRuleMap = new HashMap<>();
        filterRuleMap.put("/user/login", "anon");
        filterRuleMap.put("/doc.html", "anon");
        filterRuleMap.put("/webjars/**", "anon");
        filterRuleMap.put("/ace/**", "anon");
        filterRuleMap.put("/swagger-resources/**", "anon");
        filterRuleMap.put("/v2/**", "anon");
        filterRuleMap.put("/**", "jwt");
		shiroFilter.setFilterChainDefinitionMap(filterRuleMap);


		return shiroFilter;
    }
	
    //解决UnavailableSecurityManagerException
    @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }
}
package com.zkb.shiro;

import com.zkb.model.User;
import com.zkb.service.TokenService;
import com.zkb.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 自定义realm
 * @author 芒果味的小可乐
 *
 */
public class ShiroRealm extends AuthorizingRealm {
	
	@Autowired
	private TokenService tokenService;

	@Autowired
	UserService userService;
	
	/**
     * 使用jwt代替原生token
     *
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }
	
	//认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String credentials = (String)token.getCredentials();

        String username = tokenService.getUsername(credentials);

        User userAth = new User();
        userAth.setUsername(username);
        User user = userService.findByUsername(userAth);
        if(user == null) {
			 throw new UnknownAccountException("账号不存在");
		}
        
        if(!tokenService.verify(credentials, user.getUsername(), user.getPassword())) {
        	throw new AuthenticationException("用户名/密码错误");
        }

        return new SimpleAuthenticationInfo(credentials, credentials, getName());
	}
  
	//授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
		// TODO Auto-generated method stub
		return null;
	}
}

这里就OK了

run起来   输入127.0.0.1:8081/doc.html  发现是没有被拦截的吧

 如果不设置token的话

 这个没过滤的地址可以发现是访问不了的,直接就报错了

 不带token或token无效是不让去访问这个接口的

把用户登录之后的token填进去

接口就能正常访问了

 当然这里只是写了一个非常简单的demo来了解怎么用的

具体权限当然不可能这么简单,会涉及到用户,角色,角色权限(菜单)是动态的,可以给用户分配不同的角色,每个角色又又不同的菜单,控制的非常细的话可以到按钮到数据

 

参见:

anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

roles(角色):例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

perms(权限):例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。

port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString

是你访问的url里的?后面的参数。

authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

 

demo参见:https://download.csdn.net/download/qq_14926283/13606161

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斗码士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值