ShiroAdminToken验证类:负责装填验证信息 并传给Shiro进行处理验证

package com.xiwi.vip.ziti.common.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

public class ShiroAdminToken extends UsernamePasswordToken {
private String tokenType; // 这里的作用只是作为验证的标识(让Shiro到时候判断要用那个Realm去验证信息而已)

public ShiroAdminToken(final String username, final String password, String tokenType) {
super(username, password);
this.tokenType = tokenType;
}

public String getTokenType() {
return tokenType;
}

public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
}

用户使用账号密码进行登录。(前端请求服务器,服务器收到请求,通过 ShiroAdminToken验证类 填写验证信息交给Shiro进行验证登录)


Subject subject = SecurityUtils.getSubject();
ShiroAdminToken shiroAdminToken = new ShiroAdminToken(
paramsMap.get("account").toString(),
paramsMap.get("password").toString(),
"AdminUser"
);
try {
subject.login(shiroAdminToken);
} catch (UnknownAccountException e) {
return ResultJson.response(-1, e.getMessage());
} catch (AuthenticationException e) {
return ResultJson.response(-1, e.getMessage());
} catch (AuthorizationException e) {
return ResultJson.response(-1, e.getMessage());
} catch (Exception e) {
return ResultJson.response(-1, e.getMessage());
}

AdminUser adminUser = (AdminUser) SecurityUtils.getSubject().getPrincipal();

String token = IdUtil.simpleUUID();
timedCache.put(token, adminUser, 86400);
redisUtil.set(token, adminUser, 86400);

this.adminId = adminUser.getId().longValue();
this.adminUser = adminUser;

Map<String, Object> result = new LinkedHashMap<String, Object>() {{
put("user_info", adminUser);
put("token", token);
}};

AdminUserRealm类:用户名密码验证类

package com.xiwi.vip.ziti.common.shiro.realm;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.xiwi.vip.ziti.common.core.PasswordUtil;
import com.xiwi.vip.ziti.common.core.ResultJson;
import com.xiwi.vip.ziti.entity.AdminUser;
import com.xiwi.vip.ziti.service.AdminUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class AdminUserRealm extends AuthorizingRealm {

@Resource
private AdminUserService adminUserService;

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 权限验证 自己实现
return null;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 用户验证
if (StrUtil.isBlankIfStr(authenticationToken.getPrincipal())) {
throw new UnknownAccountException("用户不存在");
}
//获取用户信息
String account = authenticationToken.getPrincipal().toString();
String password = new String((char[])authenticationToken.getCredentials());
List<AdminUser> adminUserList = adminUserService.lambdaQuery()
.eq(AdminUser::getAccount, account)
.eq(AdminUser::getDelFlag, 0)
.list();
if (adminUserList.size() != 1) {
throw new UnknownAccountException("用户不存在");
}
AdminUser adminUser = adminUserList.get(0);
if (!adminUser.getPassword().equals(PasswordUtil.encrypt(password))) {
throw new AuthenticationException("密码错误");
}
// adminUser.setPassword("");
return new SimpleAuthenticationInfo(adminUser, password, getName());
}
}

AdminTokenRealm类:协议头token登录验证类

package com.xiwi.vip.ziti.common.shiro.realm;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xiwi.vip.ziti.common.core.PasswordUtil;
import com.xiwi.vip.ziti.common.core.RedisUtil;
import com.xiwi.vip.ziti.common.core.ResultJson;
import com.xiwi.vip.ziti.common.exception.ApplicationRunTimeException;
import com.xiwi.vip.ziti.entity.AdminUser;
import com.xiwi.vip.ziti.service.AdminUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

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

public class AdminTokenRealm extends AuthorizingRealm {
@Resource
private RedisUtil redisUtil;
@Resource
private AdminUserService adminUserService;

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null; // 自己实现
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 用户验证
if (StrUtil.isBlankIfStr(authenticationToken.getPrincipal())) {
throw new ApplicationRunTimeException(ResultJson.response(-1000, "未登录"));
}
//获取用户token
String token = authenticationToken.getPrincipal().toString();
System.out.println(
"AdminTokenRealm... token: " + token
);
if (StrUtil.isBlankIfStr(token)) {
// throw new ApplicationRunTimeException(ResultJson.response(-1000, "未登录"));
throw new UnknownAccountException();
}



// 取到token 就去查缓存 没缓存就是无效的token
if (ObjectUtil.hasNull(redisUtil.get(token))) {
// throw new ApplicationRunTimeException(ResultJson.response(-1000, "未登录"));
throw new UnknownAccountException();
}

AdminUser adminUser = redisUtil.get(token, AdminUser.class);
adminUser = adminUserService.lambdaQuery()
.eq(AdminUser::getId, adminUser.getId())
.one();
System.out.println(
"AdminTokenRealm... login:" + adminUser.toString()
);

return new SimpleAuthenticationInfo(adminUser, token, getName());
}
}

多Realm的处理类

package com.xiwi.vip.ziti.common.shiro;

import com.xiwi.vip.ziti.common.shiro.realm.AdminTokenRealm;
import com.xiwi.vip.ziti.common.shiro.realm.AdminUserRealm;
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.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class UserModularRealmAuthorizer extends ModularRealmAuthenticator {
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自己自定义的Token验证类
ShiroAdminToken userToken = (ShiroAdminToken) authenticationToken;
// 登录类型 这里就是取验证类的标识
String loginType = userToken.getTokenType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
List<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
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);
}
}
}

ShiroAuthFilter拦截器:因为我这里是有带协议头的登录验证方式 所以要加这个 其它方式看情况定

package com.xiwi.vip.ziti.common.shiro;

import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xiwi.vip.ziti.common.core.RedisUtil;
import com.xiwi.vip.ziti.common.core.ResultJson;
import com.xiwi.vip.ziti.service.AdminUserService;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class ShiroAuthFilter extends AuthorizationFilter {

@Resource
private TimedCache<String, Object> timedCache;
@Resource
private RedisUtil redisUtil;
@Resource
private AdminUserService adminUserService;


@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
HttpServletRequest request1 = (HttpServletRequest) servletRequest;
String token = request1.getHeader("admin-auth");
System.out.println(
"isAccessAllowed... token: " + token
);
if (StrUtil.isBlankIfStr(token)) {
return false;
}

// 取到协议头token 就传给Shiro进行验证
ShiroAdminToken shiroAdminToken = new ShiroAdminToken(token, token, "AdminToken");
try {
getSubject(servletRequest, servletResponse).login(shiroAdminToken);
}catch (Exception e) {
return false;
}
return true;
}

@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
System.out.println("ShiroAuthFilter onAccessDenied...");

response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSONUtil.parseObj(ResultJson.response(-1000, "未登录")).toString());
response.getWriter().flush();
response.getWriter().close();
return false;
}

@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
response.getWriter().write(JSONUtil.parseObj(ResultJson.response(-1000, "未登录")).toString());
response.getWriter().flush();
response.getWriter().close();
}

protected boolean onAccessDenied2(ServletRequest request, ServletResponse response) {

return true;
}
}

package com.xiwi.vip.ziti.common.config;

import com.xiwi.vip.ziti.common.shiro.ShiroAuthFilter;
import com.xiwi.vip.ziti.common.shiro.UserModularRealmAuthorizer;
import com.xiwi.vip.ziti.common.shiro.realm.AdminTokenRealm;
import com.xiwi.vip.ziti.common.shiro.realm.AdminUserRealm;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.SecurityManager;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.*;


@Configuration
public class ShiroConfig {

// 注入自己的Realm认证类(账号密码登录的Realm)
@Bean
public AdminUserRealm adminUserRealm() {
return new AdminUserRealm();
}

// 注入自己的Realm认证类(协议头Token登录的Realm)
@Bean
public AdminTokenRealm adminTokenRealm() {
return new AdminTokenRealm();
}

// 也注入自己的Fllter过滤类 因为里面用到ioc里的东西 所以还是要加进ioc
@Bean
public ShiroAuthFilter shiroAuthFilter() {
return new ShiroAuthFilter();
}

// 多Realm
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator(){
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthorizer userModularRealmAuthorizer = new UserModularRealmAuthorizer();
userModularRealmAuthorizer.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return userModularRealmAuthorizer;
}



// 管理认证类
@Bean(name = "securityManager")
public SecurityManager securityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// defaultWebSecurityManager.setRealm(adminUserRealm());

defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator()); // 需要再realm定义之前

List<Realm> realmList = new ArrayList<>();
realmList.add(adminUserRealm());
realmList.add(adminTokenRealm());
defaultWebSecurityManager.setRealms(realmList);
return defaultWebSecurityManager;
}

// Shiro工厂
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);

Map<String, Filter> filters=new LinkedHashMap<>();
filters.put("authc", new ShiroAuthFilter());
shiroFilterFactoryBean.setFilters(filters);

Map<String, String> map = new LinkedHashMap<>();
map.put("/admin/oauth/**", "anon");
map.put("/admin/**", "authc");
map.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}

/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}