已有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
的值