前言
在系统管理中,权限是非常重要的一个环节。目前权限框架中使用比较多的有Shiro、Spring Security。🎃
本篇简单写一下SpringBoot整合Shiro权限框架小栗子🌰。
个人博客地址:SpringBoot整合Shiro权限框架
介绍Shiro
Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro 要简单的多。++更多详细介绍参考:w3cschool-shiro++
🚀开始
创建项目就不多嗦了,直接上干货~~
相关依赖
<!-- web依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- 剔除spring logging依赖,防止与log4j2冲突 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions>
</dependency>
<!-- log4j依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- shiro 安全框架-->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.6.0</version>
</dependency>
系统配置
shiro权限的主要配置部分分为:
- subject(可以认为是一个认证对象,与当前应用交互的任何东西都是subject)
- securityManager(安全管理器,所有安全相关的东西都是跟它交互的,它管理着所有的subject)
- realm(可以认为是一个安全信息数据源,主要通过校验subject是否符合安全策略。所以我们在使用shiro时,需要先配置自己的realm)。
配置Realm
话不多说上代码~ 这里先以登录认证作为粟子
@Slf4j
public class MyShiroRealm extends AuthorizingRealm {@Resourceprivate SysUserService sysUserService;/*** 授权信息配置* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 主要以登录认证作为例子, 此处暂时不做认证, 之后例子做补充return null;}/*** 登录认证信息* @param token token* @return 认证信息* @throws AuthenticationException e*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// 返回认证信息, 一般为用户名Object principal = token.getPrincipal();if (ObjectUtil.isNull(principal)) {return null;}// 获取数据库中用户信息String loginName = principal.toString();SysUser user = sysUserService.findSysUserByName(loginName);if (null == user) {log.debug("{} - 登录用户不存在!", loginName);throw new UnknownAccountException("登录用户不存在!");}// 使用盐加密 构造方法ByteSource credentialsSalt = ByteSource.Util.bytes(loginName);;SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getLoginPwd(), credentialsSalt, getName());// 验证信息//SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getLoginPwd(), getName());log.debug("验证登录信息: {} ===>>>> {}", loginName, simpleAuthenticationInfo.getPrincipals());return simpleAuthenticationInfo;}
}
配置安全策略
增加完Realm域配置,我们需要让此配置生效,所以需要将它添加给Shiro的安全管理策略。
/*** 自定义安全管理策略*/
@Bean
public SecurityManager securityManager(MyShiroRealm myShiroRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置自定义的realmsecurityManager.setRealm(myShiroRealm);return securityManager;
}
/*** 地址过滤器** @param securityManager* @return*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 设置安全管理shiroFilterFactoryBean.setSecurityManager(securityManager);// 设置登录地址shiroFilterFactoryBean.setLoginUrl("/login");// 设置主页地址shiroFilterFactoryBean.setSuccessUrl("/index");// 设置未授权的urlshiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 开放登录接口filterChainDefinitionMap.put("/doLogin", "anon");// 生成验证码接口filterChainDefinitionMap.put("/captcha/captchaImg", "anon");// 开放静态资源文件filterChainDefinitionMap.put("/assets/**", "anon");// 其余url全部拦截,必须放在最后filterChainDefinitionMap.put("/**", "authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;
}/*** shiro生命周期* @return*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();
}/*** shiro注解启用配置* @return*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;
}/**** @param securityManager* @return*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();sourceAdvisor.setSecurityManager(securityManager);return sourceAdvisor;
}
登录
设置完自定义域,以及自定义策略我们shiro就可以进行下一步了,那就是接口认证操作,这里简单定义一个接口
/*** 登录操作** @param loginName 登录名* @param loginPwd 登录密码* @return url*/
@ResponseBody
@PostMapping("/doLogin")
public Result<String> doLogin(String loginName, String loginPwd, String captcha, boolean rememberMe, HttpServletRequest request) {// 验证码校验String sessionCaptcha = (String) request.getSession().getAttribute(AppConstants.CAPTCHA_KEY);if (!StringUtils.equalsIgnoreCase(captcha, sessionCaptcha)) {return Result.error("验证码不匹配!");}UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginName, loginPwd, rememberMe);Subject subject = SecurityUtils.getSubject();try {// 认证操作subject.login(usernamePasswordToken);// 获取登录的用户SysUser user = (SysUser) subject.getPrincipal();// 将用户信息存入cookie中Session session = subject.getSession();session.setAttribute("user", user);// 删除session验证码request.getSession().removeAttribute(AppConstants.CAPTCHA_KEY);} catch (UnknownAccountException e) {log.debug("未知用户! {}", e.getMessage());return Result.error("未知用户!");} catch (IncorrectCredentialsException e) {log.debug("{} - 用户登录密码校验异常! {}", loginName, e.getMessage());return Result.error("密码校验异常!");} catch (AuthenticationException e) {log.debug("{} - 用户登录认证失败! {}", loginName, e.getMessage());return Result.error("登录失败!");} catch (Exception e) {log.debug("{} - 用户登录异常! {}", loginName, e.getMessage());return Result.error("登录异常!");}return Result.success("登录成功!");
}
测试
接口定义完,我们需要一个登录页面来完成登录认证操作,这里使用我之前写的登录页面来完成。
点击登录后观察接口执行情况:
当执行到subject.login()方法后,会进行认证操作,这时候就会执行到我们设置的自定义Realm。红框处是我们做完登录用户与数据库中比对过后,将用户信息存到了认证信息中并返回给接口。
最后页面成功跳转至新页面,我们就完成了登录认证操作啦。🕛
END
简单的完成了一个登录认证的操作,之后会慢慢更新shiro的其他功能😀