已有Shiro可直接看第3步,修改第4步

改造后支持cookie鉴权以及header鉴权,其他方式可自行修改

本文基本无讲解,专注用法

这里假设已有User表,User对象,以及对应的数据库操作类

1.引入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.1</version>
</dependency>

2.新建realm

继承org.apache.shiro.realm.AuthorizingRealm

实现doGetAuthenticationInfo(用户鉴权方法)

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
    String username = token.getPrincipal().toString();//获取用户名
    String password = String.valueOf((char[]) token.getCredentials());//获取密码
    User u = userRepository.getUserByUsername(username);//获取用户
    if (u==null){
        throw new AccountException("用户不存在");
    }
    //这里简单讲解,实际应用需要加密密码
    if (!password.equals(u.getPassword())){
        throw new AccountException("密码不正确");
    }
    //返回构建好的AuthInfo,第一个参数为用户主体,第二个为用户密码,第三个getName()即可
    return new SimpleAuthenticationInfo(u,u.getPassword(),getName());
}

实现doGetAuthorizationInfo(获取用户角色权限方法)

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    User user = (User)SecurityUtils.getSubject().getPrincipal();//获取已登录的用户,肯定不为null,无需其他判断
    SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
    //info对象 setRoles用于设置用户角色权限,setStringPermissions设置角色的菜单权限(具体我也不知道怎么说了)
    //setObjectPermissions与setStringPermissions相同作用
    return info;
}

3.自定义SessionManager(重要!!!!)

没什么可说的 直接贴代码,主要作用就是添加sessionid获取位置,首先从header中获取,其次cookie

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

/**
 * @author 帝王攻Rainy
 * @name MySessionManager
 * @description
 * @date 2020/4/26 9:02
 * @since 1.8
 */
public class MySessionManager extends DefaultWebSessionManager {
    private static final String AUTHORIZATION = "Authorization";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    public MySessionManager(){
        super();
    }
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response){
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}

4.编写ShiroConfig

import com.luoyuer.vueblogbackend.security.filter.AuthcFilter;
import com.luoyuer.vueblogbackend.security.filter.RolesFilter;
import com.luoyuer.vueblogbackend.security.realm.MyRealm;
import com.luoyuer.vueblogbackend.security.session.MySessionManager;
import org.apache.shiro.session.mgt.SessionManager;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 帝王攻Rainy
 * @name ShiroConfig
 * @description
 * @date 2020/4/26 9:08
 * @since 1.8
 */
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        //filterChainDefinitionMap的key是url,值是权限过滤器,具体用法请百度,这里不讲
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/api/user/**","roles[user]");
        filterChainDefinitionMap.put("/api/admin/**","roles[admin]");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        bean.setFilters(filterMap);
        return bean;
    }
    //注册securityManager 
    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager(){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setSessionManager(sessionManager());
        manager.setRealm(realm());
        return manager;
    }
    //这里不能注册DefaultWebSessionManager,需要改为注册自己写的sessionManager,否则sessionid依旧获取不到
    @Bean
    public SessionManager sessionManager(){
        MySessionManager sessionManager = new MySessionManager();
        return sessionManager;
    }
    //注册realm
    @Bean
    public MyRealm realm(){
        MyRealm realm = new MyRealm();
        return realm;
    }
    //开启shiro注解支持,如@RequiresPermissions,@RequiresRoles等等
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") org.apache.shiro.mgt.SecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

到这里就基本结束了,但是shiro没有权限的返回值不是我们想要的(注解式的没有权限可以使用捕获异常来统一返回格式,在shiroFilterFactoryBean中定义的url需要重写Filter来解决

Authc拦截器,下面的new ReturnVO换为自己的

import com.fasterxml.jackson.databind.ObjectMapper;
import com.luoyuer.vueblogbackend.model.ReturnVO;
import org.apache.shiro.web.filter.authc.UserFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * @author 帝王攻Rainy
 * @name MyPermFilter
 * @description
 * @date 2020/4/26 17:34
 * @since 1.8
 */
public class AuthcFilter extends UserFilter {
    //重写回到主页方法
    @Override
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(new ReturnVO("没有权限",401)));
    }
    //重写没有权限方法
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(new ReturnVO("没有权限",401)));
        return false;
    }
}

Roles拦截器同上,不过需要将继承的类改为org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

如需修改其他拦截器,重写相关拦截器即可

最后在ShiroConfig.shiroFilterFactoryBean中添加如下代码,用于替代原拦截器:

Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("authc",new AuthcFilter());
filterMap.put("roles",new RolesFilter());
bean.setFilters(filterMap);

sessionid获取顺序:

1.从header中获取Authorization的值

2.从cookie中获取JSESSIONID的值


山内有樱名为良,树本无名只待春。