Shiro2.整合登陆认证和权限控制
2018-01-01 23:47
661 查看
前言
上一篇简单了解Shiro框架的几个比较重要的类,清楚在使用Shiro应该配置哪些。本篇则从实际角度出发开发整合登陆认证和权限控制案例。本实例环境:IDEA + Maven
本实例采用的主要技术:SpringBoot +Shiro+Thymeleaf
本次学习目标
登陆之后才有权访问控制链接,否则跳转到登录页面;
对Url链接进行权限控制,仅当前用户有该链接的访问权限才能访问,否则跳转到指定页面403 forbidden页面;
登陆过程中用户名密码错误或账号锁定情况时返回相应相应message。
pom引入Shiro依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!--Shiro辅助包配置RedisManager--> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>2.4.2.1-RELEASE</version> </dependency>
Shiro配置
@Configuration @ConfigurationProperties(prefix = "spring.redis") public class ShiroConfig { /** * 获取资源接口; */ @Autowired(required = false) private ResourcesService resourcesService; private String host; private int port; private int timeout; /** * shiro生命周期; * @return */ @Bean(name = "lifecycleBeanPostProcessor") public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean * @return */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * 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.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/usersPage"); // 未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/css/**","anon"); filterChainDefinitionMap.put("/js/**","anon"); filterChainDefinitionMap.put("/img/**","anon"); filterChainDefinitionMap.put("/font-awesome/**","anon"); //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> //自定义加载权限资源关系 List<Resources> resourcesList = resourcesService.queryAll(); for(Resources resources:resourcesList){ if (StringUtil.isNotEmpty(resources.getResurl())) { String permission = "perms[" + resources.getResurl()+ "]"; filterChainDefinitionMap.put(resources.getResurl(),permission); } } filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(myShiroRealm()); // 自定义缓存实现 使用redis //securityManager.setCacheManager(cacheManager()); // 自定义session管理 使用redis securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * 凭证匹配器 * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 所以我们需要修改下doGetAuthenticationInfo中的代码; * ) * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * @param * @return */ @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 配置shiro redisManager * 使用的是shiro-redis开源插件 * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);// 配置缓存过期时间 redisManager.setTimeout(timeout); // redisManager.setPassword(password); return redisManager; } /** * cacheManager 缓存 redis实现 * 使用的是shiro-redis开源插件 * @return */ public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * RedisSessionDAO shiro sessionDao层的实现 通过redis * 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; }
在
ShiroConfig配置类首先从
securityManager方法看,在该方法中需要设置Realm,SessionManager,CacheManager等等,因为SecurityManager是Shiro的主入口,典型的Facade模式,几乎所有相关的权限操作,都由他代理了。
其次在
shirFilter方法中通过ShiroFilterFactoryBean创建shiroFilter,Shiro默认fitler配置有这些:
public enum DefaultFilter { anon(AnonymousFilter.class), authc(FormAuthenticationFilter.class), authcBasic(BasicHttpAuthenticationFilter.class), logout(LogoutFilter.class), noSessionCreation(NoSessionCreationFilter.class), perms(PermissionsAuthorizationFilter.class), port(PortFilter.class), rest(HttpMethodPermissionFilter.class), roles(RolesAuthorizationFilter.class), ssl(SslFilter.class), user(UserFilter.class); ··· }
其权限过滤器及配置释义:
权限写法 | 常见用法 | 备注 |
---|---|---|
anon | /admins/**=anon | 没有参数,表示可以匿名使用 |
authc | /admins/user/**=authc | 表示需要认证(登录)才能使用,没有参数 |
roles | /admins/user/**=roles[“admin,guest”] | 每个参数通过才算通过,相当于hasAllRoles()方法。 |
perms | /admins/user/*=perms[“user:add:,user:modify:*”] | 当有多个参数时必须每个参数都通过才通过,想当isPermitedAll()方法 |
rest: | /admins/user/**=rest[user] | 根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。 |
authcBasic | /admins/user/**=authcBasic | 没有参数表示httpBasic认证 |
ssl | /admins/user/**=ssl | 没有参数,表示安全的url请求,协议为https |
user | /admins/user/**=user | 没有参数表示必须存在用户,当登入操作时不做检查 |
port | /admins/user/**=port[8081] | 当请求的url的端口不是8081则跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数 |
在Controller层接口接受VO请求数据, 在认证过程中,用户需要提交实体信息(
Principals)和凭据信息(
Credentials)以检验用户是否合法。
UsernamePasswordToken支持最常见的用户名/密码的认证机制,通过
subject.login(token)提交认证操作。
/** * POST提交数据; * * @param request requst请求; * @param user user对象数据; * @param model * @return */ @PostMapping(value="/login") public String login(HttpServletRequest request, User user, Model model){ if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) { request.setAttribute("msg", "用户名或密码不能为空!"); return "login"; } Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword()); try { subject.login(token); return "redirect:usersPage"; }catch (LockedAccountException lae) { token.clear(); request.setAttribute("msg", "用户已经被锁定不能登录,请与管理员联系!"); return "login"; } catch (AuthenticationException e) { token.clear(); request.setAttribute("msg", "用户或密码不正确!"); return "login"; } }
目前还没做前后端分离,所以返回时通过页面跳转来实现。认证通过后跳转至usersPage请求对应的页面,错误则跳转至login页面。
Realm认证,授权
第一篇提到过,在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的,通过覆写
doGetAuthenticationInfo完成认证操作,
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //获取用户的输入的账号. String username = (String)token.getPrincipal(); User user = userService.selectByUsername(username); if(user==null){ throw new UnknownAccountException(); } if (0==user.getEnable()) { throw new LockedAccountException(); // 帐号锁定 } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user, //用户 user.getPassword(), //密码 ByteSource.Util.bytes(username), getName() //realm name ); // 当验证都通过后,把用户信息放在session里 Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("userSession", user); session.setAttribute("userSessionId", user.getId()); return authenticationInfo; }
该方法主要执行以下操作:
1. 通过
token.getPrincipal()获取令牌信息,该数据对应Controller层
UsernamePasswordToken(user.getUsername(),user.getPassword());中的
user.getUsername();
2. 根据令牌信息从数据源(通常为数据库)中获取用户对象 ;
3. 针对返回结果进行验证;
4. 验证通过将返回一个封装了用户信息的
AuthenticationInfo实例;
5. 验证失败则抛出
AuthenticationException异常信息。
shiroFilter拦截配置
/** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是报错的,因为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * 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.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/usersPage"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> //自定义加载权限资源关系 List<Resources> resourcesList = resourcesService.queryAll(); for(Resources resources:resourcesList){ if (StringUtil.isNotEmpty(resources.getResurl())) { String permission = "perms[" + resources.getResurl()+ "]"; filterChainDefinitionMap.put(resources.getResurl(),permission); } } filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
到此关键配置已完成,接下来运行程序进行验证。
项目下载地址:springboot-shiro-2 整合登陆认证和权限控制
相关文章推荐
- springmvc整合shiro做登陆权限控制,使用mongodb存储session
- SpringBoot+shiro整合学习之登录认证和权限控制
- SpringBoot+shiro整合学习之登录认证和权限控制
- SpringBoot+shiro整合学习之登录认证和权限控制
- SpringBoot+shiro整合学习之登录认证和权限控制
- SpringBoot+shiro整合学习之登录认证和权限控制
- Spring整合Shiro做权限控制模块详细案例分析
- springboot(十四):springboot整合shiro-登录认证和权限管理
- Spring Boot整合shiro-登录认证和权限管理
- 权限控制框架shiro与spring整合详解
- springboot(十四):springboot整合shiro-登录认证和权限管理
- 转载:Spring Boot (十四):springboot整合shiro-登录认证和权限管理
- springboot(十四):springboot整合shiro-登录认证和权限管理
- shiro实现app web统一登陆认证和权限管理
- Spring整合Shiro做权限控制模块详细案例分析
- springboot(十四):springboot整合shiro-登录认证和权限管理
- springboot(十四):springboot整合shiro-登录认证和权限管理
- 登陆模块,这个是很重要的模块,有shiro和spring security专门的权限认证框架
- Spring整合Shiro做权限控制模块详细案例分析
- shiro框架的权限控制(Spring整合)