0
点赞
收藏
分享

微信扫一扫

跨域情况下shiro多类型用户认证和授权

跨域的管理主要是将用户登陆后的jsId返回到前端,前端以token的形式传回的时候,重写session认证器,以区分不同的用户。由于跨域有预检机制,不携带自定义参数,所以重写shiro过滤器,放行预检请求。

配置过类型用户的认证和授权,首先:要自定义token继承shiro的usernameAndpasswordToken,然后添加标识字段。然后在选择认证器的时候,重写realm的认证中心分配器,根据token中的标识,返回指定的认证器进行登陆。登陆时priciple传入用户实体,以便于在进行分配授权认证器的时候识别出指定的授权方法。


shiroConfig配置类

/**
* @Description: TODO(shiro配置)
* @Author: 清风怎不知意
* @CreateDate: 22:55 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
@Configuration
public class ShiroConfig {
/**
* @throws
* @Description: TODO(shiro权限过滤链)
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
//配置自定义filter
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//配置shiro的核心管理器securityManager
bean.setSecurityManager(securityManager());
//配置登录的url和登录成功的url
//bean.setLoginUrl("/");
//bean.setSuccessUrl("/index");
Map<String, Filter> filtersMap = new LinkedHashMap<>();
filtersMap.put("authc", new MyAuthenticationFilter());
bean.setFilters(filtersMap);
//配置访问权限
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/admin/manager/validate","anon");
filterChainDefinitionMap.put("/manage/*","perms[manager]");

filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/**", "authc");
//将权限注入到过滤链
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}


@Bean
public SecurityManager securityManager() {
//初始化securityManager
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//添加多个Realm
List<Realm> realms = new ArrayList<>();
realms.add(managerRealm());
realms.add(shopRealm());

//设置自定义多realm下的授权管理器
CustomerAuthrizer customModularRealmAuthorizer = new CustomerAuthrizer();
//将多个realm交给自定义权限分配器管理
customModularRealmAuthorizer.setRealms(realms);
//将自定义权限分配器交给security管理
securityManager.setAuthorizer(customModularRealmAuthorizer);
//设置自定义多realm分配管理器.
securityManager.setAuthenticator(modularRealmAuthenticator());

//将realm交给security管理
securityManager.setRealms(realms);

// 自定义缓存实现 使用redis
//securityManager.setCacheManager(cacheManager())
// 自定义session管理
securityManager.setSessionManager(sessionManager());
//注入记住我管理器;
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}

/**
* 重写系统自带的Realm管理,主要针对多realm
* */
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
}

//自定义认证中心
@Bean
public ManagerRealm managerRealm() {
ManagerRealm managerRealm = new ManagerRealm();
managerRealm.setCredentialsMatcher(hashedCredentialsMatcher());//设置解密规则
return managerRealm;
}
//自定义认证中心
@Bean
public ShopRealm shopRealm() {
ShopRealm shopRealm = new ShopRealm();
shopRealm.setCredentialsMatcher(hashedCredentialsMatcher());//设置解密规则
return shopRealm;
}

/**
* @param
* @throws
* @Description: TODO(cookie管理对象 ; rememberMeManager ()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中)
*/
@Bean
public CookieRememberMeManager rememberMeManager() {

CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
return cookieRememberMeManager;
}

/**
* @param
* @throws Exception
* @Description: TODO(cookie对象 ; 这个参数是cookie的名称 , 对应前端的checkbox的name = rememberMe)
*/
@Bean
public SimpleCookie rememberMeCookie() {
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//记住我cookie生效时间10天 ,单位秒;
simpleCookie.setMaxAge(864000);
return simpleCookie;
}

//因为我们的密码是加过密的,所以,如果要Shiro验证用户身份的话,需要告诉它我们用的是md5加密的,并且是加密了两次。同时我们在自己的Realm中也通过SimpleAuthenticationInfo返回了加密时使用的盐。这样Shiro就能顺利的解密密码并验证用户名和密码是否正确了。
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
return hashedCredentialsMatcher;
}

//自定义sessionManager,主要针对跨域时,管理前端传回的jsId进行session认证
@Bean
public SessionManager sessionManager() {
MySessionManager mySessionManager = new MySessionManager();
return mySessionManager;
}

}

自定义realm认证中心分配管理器

package top.leavies.admin.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @Description: TODO(自定义认证器分配类,将不同类型的用户分配给不同类型的认证中心进行认证;realm.getName().contains(loginType) 自定义的认证器中必须对应包含用户登陆的类型字符串)
* @Author: 清风怎不知意
* @CreateDate: 22:15 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
public class UserModularRealmAuthenticator extends ModularRealmAuthenticator {
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
// 判断getRealms()是否返回为空,在shroConfig中securityManger没有setRealms的话,这里会获取不到realm
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken 这里的UserToken是AuthenticationToken的子类
UserToken userToken = (UserToken) authenticationToken;
// 登录类型
String loginType = userToken.getLoginType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
List<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
//判断当前token属于哪一个认证器,并返回该认证器
if (realm.getName().contains(loginType))
typeRealms.add(realm);
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1){
return doSingleRealmAuthentication(typeRealms.get(0), userToken);
}
else{
return doMultiRealmAuthentication(typeRealms, userToken);
}

}
}

自定义realm授权分配管理器

package top.leavies.admin.shiro;

import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import top.leavies.entity.ManagerUserEntity;
import top.leavies.entity.ShopStaffEntity;
/**
* @Description: TODO(自定义授权realm分配机制,根据不同类型用户调用不同realm中的授权机制)
* @Author: 清风怎不知意
* @CreateDate: 20:41 2018/11/27 0027
* @Location: top.leavies.admin.shiro
*/
public class CustomerAuthrizer extends ModularRealmAuthorizer {

@Override
public boolean isPermitted(PrincipalCollection principals, String permission) {
//判断realm是否为空,如果在shiroConfig中,CustomerAuthrizer没有调用setRealms方法的话,这里可能会出现获取不到realm的情况。
assertRealmsConfigured();
Object primaryPrincipal = principals.getPrimaryPrincipal();

for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
//判断当前令牌属于哪一个认证器,并放回该认证器的授权方法
if (primaryPrincipal instanceof ManagerUserEntity) {
if (realm instanceof ManagerRealm) {
return ((ManagerRealm) realm).isPermitted(principals, permission);
}
}
if (primaryPrincipal instanceof ShopStaffEntity) {
if (realm instanceof ShopRealm) {
return ((ShopRealm) realm).isPermitted(principals, permission);
}
}

}
return false;
}
}

自定义token用于区分不同类别的用户

package top.leavies.admin.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;
/**
* @Description: TODO(自定义令牌,实现根据不同角色进行不同登陆方式的功能)
* @Author: 清风怎不知意
* @CreateDate: 22:03 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
public class UserToken extends UsernamePasswordToken {
//登陆类型,判断是哪一类用户登陆
private String loginType;
//用户所属店铺,判断员工和店铺是否绑定
private String shopId;

public UserToken(){
super();
}

public UserToken(String managerName, String managerPwd) {
super(managerName,managerPwd);
}
public UserToken(String managerName, String managerPwd,String shopId) {
super(managerName,managerPwd,shopId);
}

public String getLoginType(){
return loginType;
}

public void setLoginType(String loginType) {
this.loginType = loginType;
}

public String getShopId() {
return shopId;
}

public void setShopId(String shopId) {
this.shopId = shopId;
}
}

自定义认证中心

package top.leavies.admin.shiro;

import org.apache.shiro.SecurityUtils;
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 org.apache.shiro.util.ByteSource;
import top.leavies.admin.dao.ManagerUserRep;
import top.leavies.admin.service.ManagerService;
import top.leavies.entity.ManagerUserEntity;

import javax.annotation.Resource;

/**
* @Description: TODO(自定义后台用户认证中心)
* @Author: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
public class ManagerRealm extends AuthorizingRealm {

//用户后台管理逻辑处理接口
@Resource
private ManagerService managerService;

/**
* @Description: TODO(权限认证)
* @param principalCollection
* @Return: org.apache.shiro.authz.AuthorizationInfo
* @CreateBy: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从令牌中获取传入的用户实体
ManagerUserEntity principal = (ManagerUserEntity) principalCollection.fromRealm(getName()).iterator().next();
ManagerUserEntity managerUserEntity = managerService.getActiveManagerByName(principal.getManagerName());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermission(managerUserEntity.getManagerLimit());
return simpleAuthorizationInfo;
}

/**
* @Description: TODO(登陆控制)
* @param authenticationToken
* @Return: org.apache.shiro.authc.AuthenticationInfo
* @CreateBy: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从令牌中取出用户账号,也就是用户名
String managerName = authenticationToken.getPrincipal().toString();
ManagerUserEntity managerUserEntity = managerService.getActiveManagerByName(managerName);
if(managerUserEntity != null){
//假设用户成功登陆,存入session。如果登陆失败,则在service中将session重置为空。
SecurityUtils.getSubject().getSession().setAttribute("managerInfo",managerUserEntity);
//将正确信息写入口令,并返回
System.out.println(getName());
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(managerUserEntity,managerUserEntity.getManagerPwd(),ByteSource.Util.bytes(managerUserEntity.getSalt()),getName());
return authenticationInfo;
}
return null;
}
}

自定义认证中心

package top.leavies.admin.shiro;

import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.SecurityUtils;
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 org.apache.shiro.util.ByteSource;
import top.leavies.admin.service.ShopStaffService;
import top.leavies.entity.ShopStaffEntity;

import javax.annotation.Resource;
import java.util.Collection;

/**
* @Description: TODO(自定义店铺职工认证中心)
* @Author: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
public class ShopRealm extends AuthorizingRealm {

//用户后台管理逻辑处理接口
@Resource
private ShopStaffService shopStaffService;

/**
* @Description: TODO(权限认证)
* @param principalCollection
* @Return: org.apache.shiro.authz.AuthorizationInfo
* @CreateBy: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从令牌中获取指定realm认证中心的身份集合,并取出身份信息
ShopStaffEntity principal = (ShopStaffEntity) principalCollection.fromRealm(getName()).iterator().next();
ShopStaffEntity shopStaffEntity = shopStaffService.getActiveStaffByName(principal.getStaffName(),principal.getShopId());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermission(shopStaffEntity.getStaffLimit());
return simpleAuthorizationInfo;
}

/**
* @Description: TODO(登陆控制)
* @param authenticationToken
* @Return: org.apache.shiro.authc.AuthenticationInfo
* @CreateBy: 清风怎不知意
* @CreateDate: 21:51 2018/11/9 0009
* @Location: top.leavies.admin.shiro
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从令牌中取出用户账号,也就是用户名
UserToken userToken = (UserToken) authenticationToken;
if(userToken==null) return null;
ShopStaffEntity shopStaffEntity = shopStaffService.getActiveStaffByName(userToken.getUsername(),userToken.getShopId());
if(shopStaffEntity != null){
//假设用户成功登陆,存入session。如果登陆失败,则在service中将session重置为空。
SecurityUtils.getSubject().getSession().setAttribute("staffInfo",shopStaffEntity);
//将正确信息写入口令,并返回
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(shopStaffEntity,shopStaffEntity.getStaffPwd(),ByteSource.Util.bytes(shopStaffEntity.getSalt()),getName());
return authenticationInfo;
}
return null;
}
}

自定义跨域预检过滤器

package top.leavies.admin.shiro;

import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Title: MyAuthenticationFilter
* @Package top.leavies.admin.shiro
* @Description: TODO(自定义认证拦截过滤器,放行跨域预检请求)
* @author 清风怎不知意
* @date 2018/11/28 9:41
* Copyright (c) ©1994-2018 Scjydz.com All Rights Reserved.
*/
public class MyAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (request instanceof HttpServletRequest) {
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
return true;
}
}
return super.isAccessAllowed(request, response, mappedValue);
}

@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}

自定义sessionManager,用于跨域时处理前端返回的token

package top.leavies.admin.shiro;

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

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

/**
* @Description: TODO(自定义shiro获取sessionId的方法,获取前端带回的服务端jSessionId,适用于前后端跨域)
* @Author: 清风怎不知意
* @CreateDate: 21:49 2018/11/10 0010
* @Location: top.leavies.admin.shiro
*/
public class MySessionManager extends DefaultWebSessionManager {
private static final String AUTHORIZATION = "token";

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);
//如果请求头中有 Authorization 则其值为sessionId
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);
}
}
}

举报

相关推荐

0 条评论