接续上一篇,spring boot 、mybatis-plus、shiro整合入门教程(二)——mybatis-plus常用操作 这篇主要介绍下shiro在项目中是如何使用的, 前期的主要工作是数据库表的设计,上篇文章已经给出表的设计,详细的数据库初始化sql,可以参考代码库的sql, 简单说下登录原理,用户每次登录的时候生成token,同时根据用户id更新数据库中的token有效时间和token值,用户下次登录的时候会去校验token的有效时间,不在有效期内会返回登录页重新登录刷新。 登录的代码和shiro整合的相关代码:
/** * 登录 * @param username * @param password * @param captcha * @param randomStr * @return */ @RequestMapping (value="/login", method = RequestMethod.POST) public Map<String, Object> login(String username, String password,String captcha, String randomStr) { String value = (String)redisTemplate.opsForValue().get(Constants.NUMBER_CODE_KEY + randomStr); if (StringUtils.isBlank(value)) { return WebResult.error("验证码过期"); } if (!captcha.equals(value)) { return WebResult.error("验证码不正确"); } SysUser sysUser = sysUserService.getOne(username); if(sysUser == null || !sysUser.getPassword().equals(new Sha256Hash(password, sysUser.getSalt()).toHex())) { return WebResult.error("账号或密码不正确"); } if(sysUser.getStatus() == 0){ return WebResult.error("账号已被锁定,请联系管理员"); } //生成token 保存到数据库 可优化为存储到redis和数据库 return sysUserTokenService.createToken(sysUser.getUserId()); } /** * 生成token * @param userId * @return */ public WebResult createToken(long userId) { String token = TokenGenerator.generateValue(); //当前时间 LocalDateTime now = LocalDateTime.now(); //过期时间 LocalDateTime expireTime = now.plusHours(Constants.EXPIRE); //判断是否生成token SysUserToken sysUserToken = sysUserTokenMapper.selectById(userId); if(sysUserToken == null){ sysUserToken = new SysUserToken(); sysUserToken.setUserId(userId); sysUserToken.setToken(token); sysUserToken.setUpdateTime(now); sysUserToken.setExpireTime(expireTime); baseMapper.insert(sysUserToken); }else{ sysUserToken.setToken(token); sysUserToken.setUpdateTime(now); sysUserToken.setExpireTime(expireTime); sysUserTokenMapper.updateById(sysUserToken); } return new WebResult().put("token", token).put("expire", expireTime); }项目中使用shiro需要继承AuthenticatingFilter和AuthorizingRealm,代码如下:
/** * @author liuyi * @date 2019/5/15 * * oauth 过滤器 */ @Slf4j public class OAuthFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ return null; } return new OAuthToken(token); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { if (request instanceof HttpServletRequest) { if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) { return true; } } return super.isAccessAllowed(request, response, mappedValue); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取请求token,如果token不存在,直接返回401 String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ HttpServletResponse httpResponse = (HttpServletResponse) response; String json = JSON.toJSONString(WebResult.error(HttpStatus.SC_UNAUTHORIZED, "invalid token")); httpResponse.getWriter().print(json); return false; } return executeLogin(request, response); } @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ep, ServletRequest request, ServletResponse response) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setContentType("application/json;charset=utf-8"); try { //处理登录失败的异常 Throwable throwable = ep.getCause() == null ? ep : ep.getCause(); WebResult result = WebResult.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage()); String json = JSON.toJSONString(result); httpResponse.getWriter().print(json); } catch (IOException e) { log.error("OAuthFilter 异常", e); } return false; } /** * 获取请求的token * @param httpRequest * @return */ private String getRequestToken(HttpServletRequest httpRequest){ //从header中获取token String token = httpRequest.getHeader("token"); //如果header中不存在token,则从参数中获取token if(StringUtils.isBlank(token)){ token = httpRequest.getParameter("token"); } return token; } } @Component public class OAuthRealm extends AuthorizingRealm { @Resource private ShiroServiceImpl shiroService; @Override public boolean supports(AuthenticationToken token) { return token instanceof OAuthToken; } /** * 授权(验证权限时调用) * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SysUser user = (SysUser)principals.getPrimaryPrincipal(); Long userId = user.getUserId(); //用户权限列表 Set<String> permsSet = shiroService.getUserPermissions(userId); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); return info; } /** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String accessToken = (String) token.getPrincipal(); //根据accessToken,查询用户信息 SysUserToken tokenEntity = shiroService.queryByToken(accessToken); //token失效 if(tokenEntity == null || tokenEntity.getExpireTime().toInstant(ZoneOffset.of("+8")).toEpochMilli() < System.currentTimeMillis()){ throw new IncorrectCredentialsException("token失效,请重新登录"); } //查询用户信息 SysUser user = shiroService.queryUser(tokenEntity.getUserId()); //账号锁定 if(user.getStatus() == 0){ throw new LockedAccountException("账号已被锁定,请联系管理员"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName()); return info; } } public class OAuthToken implements AuthenticationToken { private String token; public OAuthToken(String token) { this.token = token; } @Override public String getPrincipal() { return token; } @Override public Object getCredentials() { return token; } } /** * @author liuyi * @date 2019/5/15 */ @Configuration public class ShiroConfig { /** * 权限过滤器 * @param securityManager * @return */ @Bean(name="shiroFilter") public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); shiroFilter.setLoginUrl("/sys/unauthorized"); shiroFilter.setUnauthorizedUrl("/sys/unauthorized"); //oauth过滤 Map<String, Filter> filters = new HashMap<>(); filters.put("oauth", new OAuthFilter()); shiroFilter.setFilters(filters); Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/actuator/**", "anon"); //APP 模块开放 后面通过拦截器管理 filterMap.put("/app/**", "anon"); //用户密码登录 filterMap.put("/sys/login", "anon"); //未认证 filterMap.put("/sys/unauthorized", "anon"); //验证码 filterMap.put("/sys/captcha/**", "anon"); filterMap.put("/v2/**", "anon"); filterMap.put("/", "anon"); filterMap.put("/**", "oauth"); shiroFilter.setFilterChainDefinitionMap(filterMap); return shiroFilter; } /** * 配置安全事务管理器 * @param authRealm * @param sessionManager * @return */ @Bean("securityManager") public SecurityManager securityManager(OAuthRealm authRealm, SessionManager sessionManager) { DefaultWebSecurityManager manager=new DefaultWebSecurityManager(); manager.setRealm(authRealm); manager.setSessionManager(sessionManager); return manager; } /** * session管理 * @return */ @Bean("sessionManager") public SessionManager sessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setSessionIdCookieEnabled(true); return sessionManager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } } /** * @author liuyi * @date 2019/5/15 */ @Configuration public class FilterConfig { @Bean public FilterRegistrationBean shiroFilterRegistration() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new DelegatingFilterProxy("shiroFilter")); registrationBean.addInitParameter("targetFilterLifecycle", "true"); registrationBean.setEnabled(true); registrationBean.setOrder(Integer.MAX_VALUE - 1); registrationBean.addUrlPatterns("/*"); return registrationBean; } @Bean public FilterRegistrationBean xssFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setDispatcherTypes(DispatcherType.REQUEST); registration.setFilter(new XssFilter()); registration.addUrlPatterns("/*"); registration.setName("xssFilter"); registration.setOrder(Integer.MAX_VALUE); return registration; } }代码地址 下一篇