springboot+shiro登录校验及权限验证
shiro所需要的包
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-cas</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency>
public class AuthRealm extends AuthorizingRealm { @Autowired private ISysAdminService adminService; @Autowired private ISysAdminRoleService adminRoleService; @Autowired private ISysRolePermService rolePermService; //认证.登录 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("-----------------认证.登录-------------------"); UsernamePasswordToken utoken = (UsernamePasswordToken) token;//获取用户输入的token String username = utoken.getUsername(); //处理session SessionsSecurityManager securityManager = (SessionsSecurityManager) SecurityUtils.getSecurityManager(); DefaultSessionManager sessionManager = (DefaultSessionManager) securityManager.getSessionManager(); //获取当前已登录的用户session列表 Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions(); for (Session session : sessions) { //清除该用户以前登录时保存的session // IotdUserEntity en=(IotdUserEntity)(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)); // String phone=en.getPhone(); //如果和当前session是同一个session,则不剔除 if (SecurityUtils.getSubject().getSession().getId().equals(session.getId())) break; SysAdmin admin = (SysAdmin) (session.getAttribute("sysAdmin")); if (admin != null) { String adminName = admin.getLoginName(); if (username.equals(adminName)) { System.out.println(username + "已登录,剔除中..........."); sessionManager.getSessionDAO().delete(session); } } } //获取用户信息 SysAdmin sysAdmin = adminService.getByLoginName(username); if(sysAdmin == null) { throw new UnknownAccountException(); } if(sysAdmin.getIsLock() == 1) {//被锁定 throw new LockedAccountException(); } if(sysAdmin.getIsEnable() == 1) {//该账号未被启用 throw new DisabledAccountException(); } //放入shiro.调用CredentialsMatcher检验密码 SimpleAuthenticationInfo rst = new SimpleAuthenticationInfo(sysAdmin, sysAdmin.getPassword(), this.getClass().getName()); return rst; } //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); SysAdmin admin = (SysAdmin) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户 //获取该用户的所有角色 List<Integer> listId = adminRoleService.getRoleIdByAdminId(admin.getSysAdminId()); //查询该用户的权限 List<String> list = rolePermService.getAuthCodeListByRoles(listId); info.addStringPermissions(list); return info; } }
/** * shiro配置类 * @author Administrator * */ @Configuration public class ShiroConfiguration { /** * 创建ShiroFilterFactoryBean */ @Bean(name="shiroFilter") public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); /** * shiro常用拦截器 * anon: 无需认证就可访问 * authc: 需要认证才能访问 * user :使用rememberMe的功能可直接访问 * perms: 必须有资源的权限才可以访问 * role : 该资源必须得到角色的权限才可以访问 */ //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; } //配置核心安全事务管理器 @Bean(name="securityManager") public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) { System.err.println("--------------shiro已经加载----------------"); DefaultWebSecurityManager manager=new DefaultWebSecurityManager(); manager.setRealm(authRealm); manager.setSessionManager(sessionManager()); // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 --> manager.setCacheManager(ehCacheManager()); //注入记住我管理器; manager.setRememberMeManager(rememberMeManager()); return manager; } @Bean(name = "sessionManager") public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager=new DefaultWebSessionManager(); return sessionManager; } //配置自定义的权限登录器 @Bean(name="authRealm") public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) { AuthRealm authRealm=new AuthRealm(); authRealm.setCredentialsMatcher(matcher); return authRealm; } //配置自定义的密码比较器 @Bean(name="credentialsMatcher") public CredentialsMatcher credentialsMatcher() { return new CredentialsMatcher(ehCacheManager()); } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) { AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(manager); return advisor; } //----------------------------------------------- /** * LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类, * 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。 * 主要是AuthorizingRealm类的子类,以及EhCacheManager类。 * * @return */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm, 负责用户的认证和权限的处理 * * @return */ @Bean(name = "myShiroRealm") @DependsOn("lifecycleBeanPostProcessor") public AuthRealm myShiroRealm() { AuthRealm myShiroRealm = new AuthRealm(); return myShiroRealm; } /** * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来, * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。 * * @return */ @Bean(name = "ehCacheManager") @DependsOn("lifecycleBeanPostProcessor") public EhCacheManager ehCacheManager() { EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); return cacheManager; } /** * 自定义shiroSession的cookie,如果不设置默认JSESSIONID, * 会与tomcat等默认cookie名重复,sessionIdCookie用于保存sessionId标识 * * @return */ @Bean(name = "rememberMeCookie") public SimpleCookie rememberMeCookie() { // 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); // setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点: // 设为true后,只能通过http访问,javascript无法访问 // 防止xss读取cookie simpleCookie.setHttpOnly(true); // <!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(259200); return simpleCookie; } /** * cookie管理对象; * 记住我的配置 * @return */ @Bean(name = "rememberMeManager") public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager; } /** * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。 * * @return */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); daap.setProxyTargetClass(true); return daap; } /** * thymeleaf模板使用shiro注解 * @return */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
/** * 自定义密码比较 * @author Administrator * * */ public class CredentialsMatcher extends SimpleCredentialsMatcher { @Autowired private ISysAdminService adminService; private Cache<String, AtomicInteger> passwordRetryCache; public CredentialsMatcher(CacheManager cacheManager) { passwordRetryCache = cacheManager.getCache("passwordRetryCache"); } @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { // 获取登录用户的用户名 String username = (String)token.getPrincipal(); // 获取用户登录次数 AtomicInteger retryCount = passwordRetryCache.get(username); if (retryCount == null) { // 如果用户没有登陆过,登陆次数加1 并放入缓存 retryCount = new AtomicInteger(0); passwordRetryCache.put(username, retryCount); } if (retryCount.incrementAndGet() > 3) { // 如果用户登陆失败次数大于3次 抛出锁定用户异常 并修改数据库字段 SysAdmin admin = adminService.getByLoginName(username); if (admin != null && admin.getIsLock() == 0){ // 数据库字段 默认为 0 就是未锁定状态 所以 要改为1 // 修改数据库的状态字段为锁定 admin.setIsLock((byte) 1); adminService.updateById(admin); } // 抛出用户锁定异常 throw new LockedAccountException(); } // 判断用户账号和密码是否正确 boolean matches = super.doCredentialsMatch(token, info); if (matches) { // 如果正确,从缓存中将用户登录计数 清除 passwordRetryCache.remove(username); } return matches; } /** * 根据用户名 解锁用户 * @param username * @return */ public void unlockAccount(String username){ passwordRetryCache.remove(username); } }
/** * 生成验证码 */ @RequestMapping(value = "/getVerify") public void getVerify(HttpServletRequest request, HttpServletResponse response) { try { response.setContentType("image/jpeg");// 设置相应类型,告诉浏览器输出的内容为图片 response.setHeader("Pragma", "No-cache");// 设置响应头信息,告诉浏览器不要缓存此内容 response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expire", 0); RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil(); randomValidateCode.getRandcode(request, response);// 输出验证码图片方法 } catch (Exception e) { result(HTTP_VALID,"获取验证码失败",ICON_ERROR); } } /** * 登录校验 * @param admin * @return * @throws Exception */ @RequestMapping(value="/checkLogin" ,method=RequestMethod.POST) @ResponseBody // @OperLog(operModul="登录",operType="checkLogin",operDesc="验证用户名密码") public AjaxResult checkLogin(HttpSession session ,SysAdmin admin,String captcha){ try { // String random = (String) session.getAttribute("RANDOMVALIDATECODEKEY"); // if(captcha.equals("") || !random.equals(captcha)) { // return result(HTTP_VALID,"验证码错误",ICON_EXIST); // } /** * shiro 认证操作 */ //1.获取Subject对象 Subject subject = SecurityUtils.getSubject(); //2.封装用户数量 UsernamePasswordToken token = new UsernamePasswordToken(admin.getLoginName(), DesAesUtil.encryptDES(admin.getPassword())); //3.执行登录方法 subject.login(token); SysAdmin loginAdmin = (SysAdmin)SecurityUtils.getSubject().getPrincipal(); session.setAttribute("sysAdmin", loginAdmin); JSONObject json = new JSONObject(); json.put("url", "/Bmc/sys-menu/menu"); return result(HTTP_SUCCESS , MSG_SUCCESS , ICON_SUCCESS , json); }catch (UnknownAccountException e) { return result(HTTP_VALID , "用户名不存在" , ICON_EXIST); }catch(IncorrectCredentialsException e) { return result(HTTP_VALID , "密码不正确" , ICON_EXIST); }catch(NullPointerException e) { return result(HTTP_VALID , "验证码过期" , ICON_EXIST); }catch(LockedAccountException e) { return result(HTTP_VALID , "该账号由于登录次数过多已被锁定" , ICON_EXIST); }catch(DisabledAccountException e) { return result(HTTP_VALID , "该账号未被启用" , ICON_EXIST); }catch(Exception e) { return result(HTTP_VALID , "该账号存在异常" , ICON_EXIST); } }
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" /> <!-- 登录失败次数缓存 注意 timeToLiveSeconds 设置为300秒 也就是5分钟 可以根据自己的需求更改 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="0" timeToLiveSeconds="300" overflowToDisk="false" statistics="true"> </cache> </ehcache>