持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
1、前言
在一个项目中,用户的登录认证与角色权限控制是一个最基础同时也非常重要的功能。在笔者以往参与开发的项目中,有公司自己封装的基于Redis+Session的,也有使用实现最简单Session存用户名这种的。
当然,如果是类似公司内部使用量不是很大的后台管理系统,使用上面;来实现权限管理是没有问题的。如果是稍大型的项目,还是推荐使用Shiro或者Spring Security框架。一是,这些框架有一套很成熟的权限管理机制,我们只需要去关心业务代码即可。同时,这些开源框架的社区也很活跃,有什么问题而比较容易解决。
本篇,为大家介绍下我在SpringBoot项目中如何使用Shiro来实现用户的认证、授权的。
2、Shiro简介
Shiro 是 Apache 旗下的一个开源安全认证框架,是安全认证方面的一个 Java 类库,实现用户身份认证,权限授权,加密,会话管理(Session)等功能。下面,是 Shiro 中的一些核心概念。
2.1、Realm
领域,处理用户的认证、授权,需要继承 AuthorizingRealm 类,自行重写认证、授权方法。(从数据库获取用户权限信息、密码加密校验等)
2.2、SecurityManager
安全管理器,Shiro 的核心,对用户的认证、授权进行安全管理。
2.3、SessionManager
会话管理器,Shiro 框架自己实现的不依赖Web容器的会话管理,并且可以将Session缓存搭配Redis中,所以非web项目也可以使用Shiro。
2.4、CacheManager
缓存管理。可以将用户的权限信息缓存到内存或者Redis中。
3、整合Shiro
3.1、引入依赖
<!-- Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.1</version>
</dependency>
<!-- Shiro使用Spring框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.9.1</version>
</dependency>
3.2、配置类
@Configuration
public class ShiroConfig {
/**
* 匿名权限
*/
private final static String ANON = "anon";
/**
* 安全管理器
* @param realm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(ShiroRealm realm)
{
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm
securityManager.setRealm(realm);
return securityManager;
}
/**
* 退出过滤器
*/
public LogoutFilter logoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(loginUrl);
return logoutFilter;
}
/**
* Shiro过滤器配置
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口
factoryBean.setSecurityManager(securityManager);
// 身份认证失败,则跳转到登录页面的配置
factoryBean.setLoginUrl("/login");
// 权限认证失败,则跳转到指定页面
factoryBean.setUnauthorizedUrl("/login");
// Shiro连接约束配置,即过滤链的定义
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 对静态资源设置匿名访问
filterChainDefinitionMap.put("/static/**", ANON);
filterChainDefinitionMap.put("/images/**", ANON);
filterChainDefinitionMap.put("/css/**", ANON);
filterChainDefinitionMap.put("/lib/**", ANON);
filterChainDefinitionMap.put("/js/**", ANON);
filterChainDefinitionMap.put("/logout", ANON);
filterChainDefinitionMap.put("/login", ANON);
filterChainDefinitionMap.put("/**", "user");
Map<String, Filter> filterMap = new LinkedHashMap<>();
filterMap.put("logout", new LogoutFilter());
factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
factoryBean.setFilters(filterMap);
factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return factoryBean;
}
/**
* 开启Shiro注解通知器
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
3.3、ShiroRealm
@Component
public class ShiroRealm extends AuthorizingRealm {
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SysUser user = ShiroService.getSysUser();
Set<String> roles = roleService.queryRoles(user.getUserId());
Set<String> menus = menuService.queryMenus(user.getUserId());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置角色
info.setRoles(roles);
// 设置权限
info.setStringPermissions(menus);
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken uToken = (UsernamePasswordToken) token;
String username = uToken.getUsername();
String password = uToken.getPassword();
if (upToken.getPassword() != null) {
password = new String();
}
SysUser user = shiroService.login(username, password);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
}
3.4、LogoutFilter
public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter {
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
Subject subject;
try {
subject = getSubject(request, response);
// 退出登录
subject.logout();
} catch (Exception e) {
log.error("退出登录异常,异常信息:{}.", e。getMessage(), e);
}
return false;
}
}