保健品做哪个网站好,vs网站怎么做,梅州做网站,泰安网络营销推广经过前面学习 Apache Shiro #xff0c;现在结合 Spring Boot 使用在项目里#xff0c;进行相关配置。
正文
添加依赖
在 pom.xml 文件中添加 shiro-spring 的依赖#xff1a; dependencygroupIdorg.apache.shiro/groupIdartifactIdshir…经过前面学习 Apache Shiro 现在结合 Spring Boot 使用在项目里进行相关配置。
正文
添加依赖
在 pom.xml 文件中添加 shiro-spring 的依赖 dependencygroupIdorg.apache.shiro/groupIdartifactIdshiro-spring/artifactIdversion${shiro.version}/version/dependencyRBAC
RBAC 1 是基于角色的访问控制权限与角色关联给用户配置相关角色来获取权限信息。
Shiro 配置
新建一个新的 Shiro 配置类 ShiroConfig:
package com.wuwii.common.config;import com.wuwii.module.sys.autho2.OAuth2Realm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;
import java.util.Map;/*** Apache Shiro 核心通过 Filter 来实现就好像SpringMvc 通过DispachServlet 来主控制一样。* 既然是使用 Filter 一般也就能猜到是通过URL规则来进行过滤和权限校验* 所以我们需要定义一系列关于URL的规则和访问权限。** author KronChan* version 1.0* since pre2018/2/9 10:35/pre*/
Configuration
public class ShiroConfig {Beanpublic SessionManager sessionManager() {DefaultWebSessionManager sessionManager new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(true);sessionManager.setSessionIdCookieEnabled(true);return sessionManager;}/*** 注册安全管理,必须设置 SecurityManager** param oAuth2Realm 认证* param sessionManager 缓存* return*/Beanpublic SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {DefaultWebSecurityManager securityManager new DefaultWebSecurityManager();// 可以添加多个认证执行顺序是有影响的securityManager.setRealm(oAuth2Realm);securityManager.setSessionManager(sessionManager);return securityManager;}Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);//自定义一个oauth2拦截器不设置就是使用默认的拦截器/*MapString, Filter filters new HashMap();filters.put(oauth2, new OAuth2Filter());shiroFilter.setFilters(filters);*///拦截器//!-- 过滤链定义从上向下顺序执行一般将 /**放在最为下边 --//!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--MapString, String filterMap new LinkedHashMap();//配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了filterMap.put(/sys/logout, logout);// 验证码filterMap.put(/sys/captcha.jpg, anon);// 设置系统模块下访问需要权限filterMap.put(/sys/login, anon);// 自定义的拦截//filterMap.put(/sys/**, oauth2);filterMap.put(/sys/**, authc);// 登陆的 urlshiroFilter.setLoginUrl(/sys/login);// 登陆成功跳转的 urlshiroFilter.setSuccessUrl(/);// 未授权的 url// shiroFilter.setUnauthorizedUrl(/login.html);//未授权界面;shiroFilter.setUnauthorizedUrl(/403);shiroFilter.setFilterChainDefinitionMap(filterMap);return shiroFilter;}/*** Shiro生命周期处理器* return*/Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 开启Shiro的注解* (如RequiresRoles,RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,* 并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能** return*/Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator proxyCreator new DefaultAdvisorAutoProxyCreator();proxyCreator.setProxyTargetClass(true);return proxyCreator;}/*** 开启 shiro aop注解支持.** param securityManager* return*/Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}/*** 凭证匹配器* 由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了* 所以我们需要修改下doGetAuthenticationInfo中的代码;* * b需要在身份认证中添加 realm.setCredentialsMatcher(hashedCredentialsMatcher())/b* return*//*Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName(md5);//散列算法:这里使用MD5算法;hashedCredentialsMatcher.setHashIterations(2);//散列的次数比如散列两次相当于 md5(md5());return hashedCredentialsMatcher;}*/
}Filter Chain定义说明
一个URL可以配置多个Filter使用逗号分隔当设置多个过滤器时全部验证通过才视为通过部分过滤器可指定参数如permsroles
Shiro内置的FilterChain:
Filter NameClassanonorg.apache.shiro.web.filter.authc.AnonymousFilterauthcorg.apache.shiro.web.filter.authc.FormAuthenticationFilterauthcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilterpermsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilterportorg.apache.shiro.web.filter.authz.PortFilterrestorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterrolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFiltersslorg.apache.shiro.web.filter.authz.SslFilteruserorg.apache.shiro.web.filter.authc.UserFilter
* anon:所有url都都可以匿名访问 * authc: 需要认证才能进行访问 * user:配置记住我或认证通过可以访问
自定义的拦截器(可选)
如果需要按照自己的需要定义一个 oauth2 的拦截器则需要 继承 AuthenticatingFilter 实现几个方法即可。
/*** oauth2过滤器*/
public class OAuth2Filter extends AuthenticatingFilter {/*** logger*/private static final Logger LOGGER LoggerFactory.getLogger(OAuth2Filter.class);Overrideprotected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {//获取请求tokenString token getRequestToken((HttpServletRequest) request);if (StringUtils.isBlank(token)) {return null;}return new OAuth2Token(token);}/*** shiro权限拦截核心方法 返回true允许访问resource* param request* param response* param mappedValue* return*/Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return false;}/*** 当访问拒绝时是否已经处理了* 如果返回true表示需要继续处理* 如果返回false表示该拦截器实例已经处理完成了将直接返回即可。* param request* param response* return* throws Exception*/Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//获取请求token如果token不存在直接返回401String token getRequestToken((HttpServletRequest) request);if (StringUtils.isBlank(token)) {HttpServletResponse httpResponse (HttpServletResponse) response;((HttpServletResponse) response).setStatus(401);response.getWriter().print(没有权限请联系管理员授权);return false;}return executeLogin(request, response);}/*** 鉴定失败返回错误信息* param token* param e* param request* param response* return*/Overrideprotected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {try {((HttpServletResponse) response).setStatus(401);response.getWriter().print(没有权限请联系管理员授权);} catch (IOException e1) {LOGGER.error(e1.getMessage(), e1);}return false;}/*** 获取请求的token*/private String getRequestToken(HttpServletRequest httpRequest) {//从header中获取tokenString token httpRequest.getHeader(token);//如果header中不存在token则从参数中获取tokenif (StringUtils.isBlank(token)) {return httpRequest.getParameter(token);}// 还可以实现从 cookie 获取Cookie[] cookies httpRequest.getCookies();if(nullcookies||cookies.length0){return null;}for (Cookie cookie : cookies) {if (cookie.getName().equals(token)) {token cookie.getValue();continue;}}return token;}
}具体实现可以参考我的上篇文章 《Apache Shiro的拦截器和认证》
认证实现
Shiro的认证过程最终会交由Realm执行这时会调用Realm的 getAuthenticationInfo(token) 方法。 该方法主要执行以下操作:
检查提交的进行认证的令牌信息根据令牌信息从数据源(通常为数据库)中获取用户信息对用户信息进行匹配验证。验证通过将返回一个封装了用户信息的AuthenticationInfo实例。验证失败则抛出AuthenticationException异常信息。
而在我们的应用程序中要做的就是自定义一个Realm类继承 AuthorizingRealm 抽象类重载 doGetAuthenticationInfo ()重写获取用户信息的方法。
Component
public class OAuth2Realm extends AuthorizingRealm {Resourceprivate ShiroService shiroService;Resourceprivate SysUserService sysUserService;/*** 此方法调用 hasRole,hasPermission的时候才会进行回调.** 权限信息.(授权):* 1、如果用户正常退出缓存自动清空* 2、如果用户非正常退出缓存自动清空* 3、如果我们修改了用户的权限而用户不退出系统修改的权限无法立即生效。* 需要手动编程进行实现放在service进行调用* 在权限修改后调用realm中的方法realm已经由spring管理所以从spring中获取realm实例* 调用clearCached方法* :Authorization 是授权访问控制用于对用户进行的操作授权证明该用户是否允许进行当前操作如访问某个链接某个资源文件等。*** 当没有使用缓存的时候不断刷新页面的话这个代码会不断执行* 当其实没有必要每次都重新设置权限信息所以我们需要放到缓存中进行管理* 当放到缓存中时这样的话doGetAuthorizationInfo就只会执行一次了* 缓存过期之后会再次执行。** param principals* return*/Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SysUserEntity user (SysUserEntity) (principals.getPrimaryPrincipal());;// 获取该用户权限列表SetString permsSet shiroService.getUserPermissions(user.getId());SimpleAuthorizationInfo info new SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return info;}/*** 认证回调函数,登录时调用* 首先根据传入的用户名获取User信息然后如果user为空那么抛出没找到帐号异常UnknownAccountException* 如果user找到但锁定了抛出锁定异常LockedAccountException最后生成AuthenticationInfo信息* 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配*/Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken usernamePasswordToken (UsernamePasswordToken) token;SysUserEntity user sysUserService.queryByUsername(usernamePasswordToken.getUsername());//账号不存在、密码错误if (user null) {throw new KCException(账号或密码不正确);}// 交给 shiro 自己去验证// 明文验证return new SimpleAuthenticationInfo(user, // 存入凭证的信息登陆成功后可以使用 SecurityUtils.getSubject().getPrincipal();在任何地方使用它user.getPassword(),getName());// 加密的方式// 交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配如果觉得人家的不好可以自定义实现/*return new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()), // 加盐可以注册凭证匹配器 HashedCredentialsMatcher 告诉它怎么加密的getName());*/}
}实现上面两个方法即可完成身份验证和权限验证。
登陆实现 PostMapping(/login)ApiOperation(系统登陆)public ResponseEntityString login(RequestBody SysUserLoginForm userForm) {String kaptcha ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);if (!userForm.getCaptcha().equalsIgnoreCase(kaptcha)) {throw new KCException(验证码不正确);}UsernamePasswordToken token new UsernamePasswordToken(userForm.getUsername(), userForm.getPassword());Subject currentUser SecurityUtils.getSubject();currentUser.login(token);//账号锁定if (getUser().getStatus() SysConstant.SysUserStatus.LOCK) {throw new KCException(账号已被锁定,请联系管理员);}return ResponseEntity.status(HttpStatus.OK).body(登陆成功);}权限验证 ApiOperation(用于测试查询)ApiImplicitParam(name string, value id, dataType string)RequiresPermissions(sys:user:list1)GetMapping()public ResponseEntityListSysUserEntity query(CustomValid String string) {return new ResponseEntity(sysUserService.query(new SysUserEntity()), OK);}简单测试一个例子sys:user:list1 多加一个 1 肯定会验证失败查看程序会做什么它会去我们定义的 Realm 中的 doGetAuthorizationInfo(PrincipalCollection principals) 方法中执行查询该用户的所有权限。 验证失败后最后程序结果如下
Caused by: org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method: public org.springframework.http.ResponseEntity com.wuwii.module.sys.controller.SysUserController.query(java.lang.String)at org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor.assertAuthorized(AuthorizingAnnotationMethodInterceptor.java:90)... 77 common frames omitted权限注解
RequiresAuthentication
表示当前Subject已经通过login进行了身份验证即Subject. isAuthenticated()返回true。 RequiresUser
表示当前Subject已经身份验证或者通过记住我登录的。RequiresGuest
表示当前Subject没有身份验证或通过记住我登录过即是游客身份。RequiresRoles(value{“admin”, “user”}, logical Logical.AND)
表示当前Subject需要角色admin和user。RequiresPermissions (value{“user:a”, “user:b”}, logical Logical.OR)
表示当前Subject需要权限user:a或user:b。 参考资料
[Spring Boot 集成-Shiro]39.2. Spring Boot Shiro权限管理【从零开始学Spring Boot】 Role-Based Access Control ?