参考文章:
https://blog.youkuaiyun.com/jin5203344/article/details/53174341
本篇文章我将会从两个方面去讲解,一个是从shiro的应用 第二个是我在项目中遇到的一些问题:
1.shiro的整个登录流程:

2.首先我们来看看shiro的应用
导入maven依赖

接下来shiroconfig配置
@Configuration
@Slf4j
public class ShiroConfig {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* Shiro 权限相关注解 使用
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* <p>
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setUnauthorizedUrl("/rest/unauth");
try {
loadShiroFilterChain(shiroFilterFactoryBean);
} catch (IOException e) {
log.error("io读取异常");
e.printStackTrace();
}
shiroFilterFactoryBean.setLoginUrl("/rest/login");
return shiroFilterFactoryBean;
}
public OrPermissionsAuthorizationFilter orPermissionsAuthorizationFilter() {
return new OrPermissionsAuthorizationFilter();
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置自定realm
securityManager.setRealm(getDatabaseRealm());
//自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
//自定义sessoin管理,使用redis
securityManager.setSessionManager(sessionManager());
//注入记住我管理器
// securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* 配置shiro redisManager 使用的是shiro-redis开源插件
*/
@ConfigurationProperties(prefix = "redis.shiro")
@Bean
public RedisManager redisManager() {
return new RedisManager();
}
/**
* cacheManager 缓存 redis实现 使用的是shiro-redis开源插件
*/
@Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
//自定义sessionManager
@Bean
public SessionManager sessionManager() {
MySessionManager mySessionManager = new MySessionManager();
mySessionManager.setSessionDAO(redisSessionDAO());
// mySessionManager.setSessionIdCookie(simpleCookie());
return mySessionManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* cookie对象;
*/
// @Bean
// public SimpleCookie simpleCookie() {
// //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
// SimpleCookie simpleCookie = new SimpleCookie(
// CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME);
// //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
// //setcookie()的第七个参数
// //设为true后,只能通过http访问,javascript无法访问
// //防止xss读取cookie
// // 是否只在https情况下传输
// simpleCookie.setSecure(false);
// //<!-- 记住我cookie生效时间30天 ,单位秒;-->
// simpleCookie.setMaxAge(2592000);
// return simpleCookie;
// }
/**
* cookie管理对象;记住我功能
*/
// @Bean
// public CookieRememberMeManager rememberMeManager() {
// CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
// cookieRememberMeManager.setCookie(simpleCookie());
// //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
// cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
// return cookieRememberMeManager;
// }
/**
* 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 所以我们需要修改下doGetAuthenticationInfo中的代码;
* )
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5(""));
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
@Bean
public ShiroRealm getDatabaseRealm() {
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 加载shiroFilter权限控制规则(然后从数据库读取配置)
*/
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) throws IOException {
//自定义拦截器
Map<String, Filter> customisedFilter = new HashMap<>(2);
customisedFilter.put("orperms", orPermissionsAuthorizationFilter());
customisedFilter.put("corsAuthenticationFilter", corsAuthenticationFilter());
Map<String, String> filterChainDefinitionMap = initUrl();
shiroFilterFactoryBean.setFilters(customisedFilter);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}
public CORSAuthenticationFilter corsAuthenticationFilter() {
return new CORSAuthenticationFilter();
}
/**
* 初始化url路径
*/
private Map<String, String> initUrl() throws IOException {
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
InputStream in = this.getClass().getClassLoader().getResourceAsStream("url.properties");
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader br = new BufferedReader(inputStreamReader);
String str = null;
try {
while ((str = br.readLine()) != null) {
if (StringUtils.isEmpty(str) || str.contains("#")) {
continue;
}
String[] arr = str.split("=");
filterChainDefinitionMap.put(arr[0].trim(), arr[1].trim());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
br.close();
}
}
return filterChainDefinitionMap;
}
initUrl()方法需要解释一下,在filterChainDefinitionMap中需要配置一系列的拦截路径(太长了),因此我将拦截路径配置到了配置文件中,同样我也推荐大家这样去做,这样管理起来也是非常方便.
下面就是开始
ShiroRealm.java
@Component
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private LoginInfoService loginInfoService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
System.out.println("欢迎认证");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//TODO token.setRememberMe(true)
String username = token.getPrincipal().toString();
LoginInfo loginInfo = loginInfoService.getByUserName(username);
if (loginInfo == null) {
return null;
} else {
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, loginInfo.getPassword(),
this.getClass().getSimpleName());
clearCachedAuthorizationInfo(token.getPrincipal());
return info;
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("开始授权!");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 能进入到这里,表示账号已经通过验证了
String userName = (String) principalCollection.getPrimaryPrincipal();
LoginInfo loginInfo = loginInfoService.getByUserName(userName);
String type = loginInfo.getType();
Set<String> permissions = new HashSet<>();
switch (type) {
case "1":
type = ApiConstant.HQ_FINANCE + ":" + "1";
break;
case "2":
type = ApiConstant.SUB_COMPANY_MANAGER + ":" + "2";
break;
case "3":
type = ApiConstant.SUB_COMPANY_FINANCE + ":" + "3";
break;
default:
break;
}
permissions.add(type);
//给用户添加type 代表具有该类型的权限 加入如类型为admin
info.setStringPermissions(permissions);
return info;
这样就所有的shiro就可以使用了,这里还有一个地方我需要解释,由于shiro的过滤器都是且的关系,一旦权限或者角色出现或的关系时无法解决,那这个时候shiro自带的过滤器就无法满足我们的需求了 这个时候就需要去重写我们的过滤器.
例如这里我重写了权限过滤器.
public class OrPermissionsAuthorizationFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
return true;
}
Subject subject = getSubject(request, response);
String[] perms = (String[]) mappedValue;
for (String perm : perms) {
String[] permArr = perm.split(":");
if (!isAccessAllowedRealization(permArr, subject, request, response)) {
return false;
}
}
return true;
}
/**
* 权限过滤实现
*
* @return
*/
private boolean isAccessAllowedRealization(String[] permArr, Subject subject, ServletRequest request, ServletResponse response) throws IOException {
if (permArr.length > 1) {
// 权限校验不通过返回的url链接
String unauthorizedUrl = getUnauthorizedUrl();
String username = null;
try {
username = (String) subject.getPrincipals().getPrimaryPrincipal();
} catch (NullPointerException e) {
log.error("用户试图不进行登录进入系统!");
e.printStackTrace();
return false;
}
LoginInfoService loginInfoService = SpringContextUtils.getContext().getBean(LoginInfoService.class);
LoginInfo user = loginInfoService.getByUserName(username);
if (!isContains(permArr, request, response, unauthorizedUrl, user)) return false;
}
return true;
}
private boolean isContains(String[] permArr, ServletRequest request, ServletResponse response, String unauthorizedUrl, LoginInfo user) throws IOException {
if (!Arrays.asList(permArr).contains(user.getType())) {
return false;
}
return true;
}
/**
* 会话超时或权限校验未通过的 因为在校验权限的时候已经进行redirect处理所以这边暂时不处理
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", "*");
res.setStatus(HttpServletResponse.SC_OK);
res.setHeader("content-type", "text/html;charset=UTF-8");
res.setCharacterEncoding("UTF-8");
PrintWriter writer = res.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("code", 702);
map.put("msg", "未授权");
writer.write(JSON.toJSONString(map));
writer.close();
return false;
}
那么角色过滤器呢 同样也是可以的 !
本文详细介绍了Shiro框架在项目中的应用,包括登录流程、权限配置、自定义过滤器及Shiro与Redis的整合,解决了实际开发中遇到的问题。
9134

被折叠的 条评论
为什么被折叠?



