Shiro + JWT + SpringBoot应用示例代码详解

1.Shiro的简介

Apache Shiro是一种功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理,可用于保护 从命令行应用程序,移动应用程序到Web和企业应用程序等应用的安全。

  • Authentication 身份认证/登录,验证用户是不是拥有相应的身份;
  • Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
  • Cryptography 安全数据加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  • Session Management 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;
  • Web Integration web系统集成
  • Interations 集成其它应用,spring、缓存框架

从应用程序角度的来观察如何使用Shiro完成工作:

Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个Shiro应用:

1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

2.Shiro + JWT + SpringBoot

1.导入依赖

<dependency>
 <groupId>org.apache.shiro</groupId>
 <artifactId>shiro-spring</artifactId>
 <version>1.4.1</version>
</dependency>
<dependency>
 <groupId>com.auth0</groupId>
 <artifactId>java-jwt</artifactId>
 <version>3.8.2</version>
</dependency>

2.配置JWT

public class JWTUtil {
 /**
 * 校验 token是否正确
 *
 * @param token 密钥
 * @param secret 用户的密码
 * @return 是否正确
 */
 public static boolean verify(String token, String username, String secret) {
 try {
  Algorithm algorithm = Algorithm.HMAC256(secret);
  JWTVerifier verifier = JWT.require(algorithm)
   .withClaim("username", username)
   .build();
  verifier.verify(token);
  return true;
 } catch (Exception e) {
  log.info("token is invalid{}", e.getMessage());
  return false;
 }
 }

 public static String getUsername(HttpServletRequest request) {
 // 取token
 String token = request.getHeader("Authorization");
 return getUsername(UofferUtil.decryptToken(token));
 }
 /**
 * 从 token中获取用户名
 * @return token中包含的用户名
 */
 public static String getUsername(String token) {
 try {
  DecodedJWT jwt = JWT.decode(token);
  return jwt.getClaim("username").asString();
 } catch (JWTDecodeException e) {
  log.error("error:{}", e.getMessage());
  return null;
 }
 }

 public static Integer getUserId(HttpServletRequest request) {
 // 取token
 String token = request.getHeader("Authorization");
 return getUserId(UofferUtil.decryptToken(token));
 }
 /**
 * 从 token中获取用户ID
 * @return token中包含的ID
 */
 public static Integer getUserId(String token) {
 try {
  DecodedJWT jwt = JWT.decode(token);
  return Integer.valueOf(jwt.getSubject());
 } catch (JWTDecodeException e) {
  log.error("error:{}", e.getMessage());
  return null;
 }
 }

 /**
 * 生成 token
 * @param username 用户名
 * @param secret 用户的密码
 * @return token 加密的token
 */
 public static String sign(String username, String secret, Integer userId) {
 try {
  Map<String, Object> map = new HashMap<>();
  map.put("alg", "HS256");
  map.put("typ", "JWT");
  username = StringUtils.lowerCase(username);
  Algorithm algorithm = Algorithm.HMAC256(secret);
  return JWT.create()
   .withHeader(map)
   .withClaim("username", username)
   .withSubject(String.valueOf(userId))
   .withIssuedAt(new Date())
//   .withExpiresAt(date)
   .sign(algorithm);
 } catch (Exception e) {
  log.error("error:{}", e);
  return null;
 }
 }
}

3.配置Shiro

4.实现JWTToken

token自己已经包含了用户名等信息。

@Data
public class JWTToken implements AuthenticationToken {

 private static final long serialVersionUID = 1282057025599826155L;

 private String token;

 private String exipreAt;

 public JWTToken(String token) {
 this.token = token;
 }

 public JWTToken(String token, String exipreAt) {
 this.token = token;
 this.exipreAt = exipreAt;
 }

 @Override
 public Object getPrincipal() {
 return token;
 }

 @Override
 public Object getCredentials() {
 return token;
 }

}

5.实现Realm

自定义实现 ShiroRealm,包含认证和授权两大模块。

public class ShiroRealm extends AuthorizingRealm {

 @Resource
 private RedisUtil redisUtil;

 @Autowired
 private ISysUserService userService;

 @Autowired
 private ISysRoleService roleService;

 @Autowired
 private ISysMenuService menuService;

 // 必须重写此方法,不然Shiro会报错
 @Override
 public boolean supports(AuthenticationToken token) {
 return token instanceof JWTToken;
 }

 /**
 * 只有当需要检测用户权限的时候才会调用此方法
 * 授权模块,获取用户角色和权限。
 * @param token token
 * @return AuthorizationInfo 权限信息
 */
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {
 Integer userId = JWTUtil.getUserId(token.toString());

 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

 // 获取用户角色集
 Set<String> roleSet = roleService.selectRolePermissionByUserId(userId);
 simpleAuthorizationInfo.setRoles(roleSet);

 // 获取用户权限集
 Set<String> permissionSet = menuService.findUserPermissionsByUserId(userId);
 simpleAuthorizationInfo.setStringPermissions(permissionSet);
 return simpleAuthorizationInfo;
 }

 /**
 * 用户认证:编写shiro判断逻辑,进行用户认证
 * @param authenticationToken 身份认证 token
 * @return AuthenticationInfo 身份认证信息
 * @throws AuthenticationException 认证相关异常
 */
 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
 // 这里的 token是从 JWTFilter 的 executeLogin 方法传递过来的,已经经过了解密
 String token = (String) authenticationToken.getCredentials();
 String encryptToken = UofferUtil.encryptToken(token); //加密token
 String username = JWTUtil.getUsername(token); //从token中获取username
 Integer userId = JWTUtil.getUserId(token); //从token中获取userId

 // 通过redis查看token是否过期
 HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
 String ip = IPUtil.getIpAddr(request);
 String encryptTokenInRedis = redisUtil.get(Constant.RM_TOKEN_CACHE + encryptToken + StringPool.UNDERSCORE + ip);
 if (!token.equalsIgnoreCase(UofferUtil.decryptToken(encryptTokenInRedis))) {
  throw new AuthenticationException("token已经过期");
 }

 // 如果找不到,说明已经失效
 if (StringUtils.isBlank(encryptTokenInRedis)) {
  throw new AuthenticationException("token已经过期");
 }

 if (StringUtils.isBlank(username)) {
  throw new AuthenticationException("token校验不通过");
 }

 // 通过用户id查询用户信息
 SysUser user = userService.getById(userId);

 if (user == null) {
  throw new AuthenticationException("用户名或密码错误");
 }
 if (!JWTUtil.verify(token, username, user.getPassword())) {
  throw new AuthenticationException("token校验不通过");
 }
 return new SimpleAuthenticationInfo(token, token, "febs_shiro_realm");
 }
}

6.重写Filter

所有的请求都会先经过 Filter,所以我们继承官方的 BasicHttpAuthenticationFilter ,并且重写鉴权的方法。

代码的执行流程 preHandle -> isAccessAllowed -> isLoginAttempt -> executeLogin 。

@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter {

 private static final String TOKEN = "Authorization";

 private AntPathMatcher pathMatcher = new AntPathMatcher();

 /**
 * 对跨域提供支持
 */
 @Override
 protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 HttpServletResponse httpServletResponse = (HttpServletResponse) response;
 httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
 httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
 httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
 // 跨域时会首先发送一个 option请求,这里我们给 option请求直接返回正常状态
 if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
  httpServletResponse.setStatus(HttpStatus.OK.value());
  return false;
 }
 return super.preHandle(request, response);
 }

 @Override
 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 UofferProperties UofferProperties = SpringContextUtil.getBean(UofferProperties.class);
 // 获取免认证接口 url
 // 在application.yml中配置/adminApi/auth/doLogin/**,/adminApi/auth/register/**, ...
 String[] anonUrl = StringUtils.splitByWholeSeparatorPreserveAllTokens(UofferProperties.getShiro().getAnonUrl(), ",");

 boolean match = false;
 for (String u : anonUrl) {
  if (pathMatcher.match(u, httpServletRequest.getRequestURI())) {
  match = true;
  }
 }
 if (match) {
  return true;
 }
 if (isLoginAttempt(request, response)) {
  return executeLogin(request, response);
 }
 return false;
 }

 /**
 * 判断用户是否想要登入。
 * 检测header里面是否包含Authorization字段即可
 */
 @Override
 protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
 HttpServletRequest req = (HttpServletRequest) request;
 String token = req.getHeader(TOKEN);
 return token != null;
 }

 @Override
 protected boolean executeLogin(ServletRequest request, ServletResponse response) {
 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 String token = httpServletRequest.getHeader(TOKEN); //得到token
 JWTToken jwtToken = new JWTToken(UofferUtil.decryptToken(token)); // 解密token
 try {
  // 提交给realm进行登入,如果错误他会抛出异常并被捕获
  getSubject(request, response).login(jwtToken);
  // 如果没有抛出异常则代表登入成功,返回true
  return true;
 } catch (Exception e) {
  log.error(e.getMessage());
  return false;
 }
 }

 @Override
 protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
 log.debug("Authentication required: sending 401 Authentication challenge response.");
 HttpServletResponse httpResponse = WebUtils.toHttp(response);
// httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
 httpResponse.setCharacterEncoding("utf-8");
 httpResponse.setContentType("application/json; charset=utf-8");
 final String message = "未认证,请在前端系统进行认证";
 final Integer status = 401;
 try (PrintWriter out = httpResponse.getWriter()) {
//  String responseJson = "{\"message\":\"" + message + "\"}";
  JSONObject responseJson = new JSONObject();
  responseJson.put("msg", message);
  responseJson.put("status", status);
  out.print(responseJson);
 } catch (IOException e) {
  log.error("sendChallenge error:", e);
 }
 return false;
 }
}

7. ShiroConfig

@Configuration
public class ShiroConfig {

 @Bean
 public ShiroRealm shiroRealm() {
 // 配置 Realm
 return new ShiroRealm();
 }

 // 创建DefaultWebSecurityManager
 @Bean("securityManager")
 public SecurityManager securityManager() {
 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 // 配置 SecurityManager,并注入 shiroRealm
 securityManager.setRealm(shiroRealm());
 return securityManager;
 }

 // 创建ShiroFilterFactoryBean
 @Bean
 public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

 ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
 // 设置 securityManager
 shiroFilterFactoryBean.setSecurityManager(securityManager);

 	//添加Shiro过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截器
		 * 常用的过滤器:
		 * anon: 无需认证(登录)可以访问
		 * authc: 必须认证才可以访问
		 * user: 如果使用rememberMe的功能可以直接访问
		 * perms: 该资源必须得到资源权限才可以访问
		 * role: 该资源必须得到角色权限才可以访问
		 */

 // 在 Shiro过滤器链上加入 自定义过滤器JWTFilter 并取名为jwt
 LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
 filters.put("jwt", new JWTFilter());
 shiroFilterFactoryBean.setFilters(filters);

 // 自定义url规则
 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
 // 所有请求都要经过 jwt过滤器
 filterChainDefinitionMap.put("/**", "jwt");
 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
 return shiroFilterFactoryBean;
 }

 /**
 * 下面的代码是添加注解支持
 */
 @Bean
 @DependsOn({"lifecycleBeanPostProcessor"})
 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
 // 设置代理类
 DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
 creator.setProxyTargetClass(true);

 return creator;
 }

 /**
 * 开启aop注解支持
 *
 * @param securityManager
 * @return
 */
 @Bean("authorizationAttributeSourceAdvisor")
 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
 return authorizationAttributeSourceAdvisor;
 }

 // Shiro生命周期处理器
 @Bean
 public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 return new LifecycleBeanPostProcessor();
 }

}

8.登陆

 /**
 * 登录方法
 *
 * @param username 用户名
 * @param password 密码
 * @param code 验证码
 * @param uuid 唯一标识
 * @return 结果
 */
 @PostMapping("/doLogin")
 public ResultVo login(String username, String password, String code, String uuid, HttpServletRequest request) throws UofferException {

 String verifyKey = Constant.RM_CAPTCHA_CODE_KEY + uuid;
 String captcha = redisUtil.getCacheObject(verifyKey);
 redisUtil.del(verifyKey);

 if (captcha == null) {
  return ResultVo.failed(201, "验证码失效");
 }
 if (!code.equalsIgnoreCase(captcha)) {
  return ResultVo.failed(201, "验证码错误");
 }

 username = StringUtils.lowerCase(username);
 password = MD5Util.encrypt(username, password);

 final String errorMessage = "用户名或密码错误";
 SysUser user = userManager.getUser(username);

 if (user == null) {
  return ResultVo.failed(201, errorMessage);
 }
 if (!StringUtils.equalsIgnoreCase(user.getPassword(), password)) {
  return ResultVo.failed(201, errorMessage);
 }
 if (Constant.STATUS_LOCK.equals(user.getStatus())) {
  return ResultVo.failed(201, "账号已被锁定,请联系管理员!");
 }

 Integer userId = user.getUserId();
 String ip = IPUtil.getIpAddr(request);
 String address = AddressUtil.getCityInfo(ip);
 // 更新用户登录时间
 SysUser sysUser = new SysUser();
 sysUser.setUserId(userId);
 sysUser.setLastLoginTime(new Date());
 sysUser.setLastLoginIp(ip);
 userService.updateById(sysUser);

 // 拿到token之后加密
 String sign = JWTUtil.sign(username, password, userId);
 String token = UofferUtil.encryptToken(sign);
 LocalDateTime expireTime = LocalDateTime.now().plusSeconds(properties.getShiro().getJwtTimeOut());
 String expireTimeStr = DateUtil.formatFullTime(expireTime);
 JWTToken jwtToken = new JWTToken(token, expireTimeStr);

 // 将登录日志存入日志表中
 SysLoginLog loginLog = new SysLoginLog();
 loginLog.setIp(ip);
 loginLog.setAddress(address);
 loginLog.setLoginTime(new Date());
 loginLog.setUsername(username);
 loginLog.setUserId(userId);
 loginLogService.save(loginLog);

 saveTokenToRedis(username, jwtToken, ip, address);
 JSONObject data = new JSONObject();
 data.put("Authorization", token);

 // 将用户配置及权限存入redis中
 userManager.loadOneUserRedisCache(userId);
 return ResultVo.oK(data);
 }

9.@RequiresPermissions

要求subject中必须含有bus:careerTalk:query的权限才能执行方法someMethod()。否则抛出异常AuthorizationException。

@RequiresPermissions("bus:careerTalk:query")
public void someMethod() {
}

引用:
https://www.iteye.com/blog/jinnianshilongnian-2018398
https://www.jianshu.com/p/f37f8c295057

总结

到此这篇关于Shiro + JWT + SpringBoot应用的文章就介绍到这了,更多相关Shiro JWT SpringBoot内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法

    摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的.现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能够访问API. 一:开发一个简单的API 在IDEA开发工具中新建一个maven工程,添加对应的依赖如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-b

  • Spring boot整合shiro+jwt实现前后端分离

    本文实例为大家分享了Spring boot整合shiro+jwt实现前后端分离的具体代码,供大家参考,具体内容如下 这里内容很少很多都为贴的代码,具体内容我经过了看源码和帖子加了注释.帖子就没用太多的内容 先下载shiro和jwt的jar包 <!-- shiro包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId

  • Java中基于Shiro,JWT实现微信小程序登录完整例子及实现过程

    小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html : 本文是对接微信小程序自定义登录的一个完整例子实现 ,技术栈为 : SpringBoot+Shiro+JWT+JPA+Redis. 如果对该例子比较感兴趣或者觉得言语表达比较啰嗦,可查看完整的项目地址 : https://github.com/EalenXie/shiro-jwt-applet

  • Vue+Jwt+SpringBoot+Ldap完成登录认证的示例代码

    本人野生程序员一名,了解了一些微服务架构.前后端分离.SPA的知识后就想试试做点什么东西.之前一直做后端,前端只是有基础知识.之前学习过angularjs,但当时就是一脸懵逼(完全看不懂是啥)就放弃了.最近又学了Vue,这次感觉总算明白了一些,但其中也跳过很多坑(应该还会更多),在这里写下来记录一下吧. 说回主题,之前传统登录认证的方法基本是由服务器端提供一个登录页面,页面中的一个form输入username和password后POST给服务器,服务器将这些信息与DB或Ldap中的用户信息对比,

  • SpringBoot使用JWT实现登录验证的方法示例

    什么是JWT JSON Web Token(JWT)是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间以JSON对象安全地传输信息.这些信息可以通过数字签名进行验证和信任.可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对来对JWT进行签名. 具体的jwt介绍可以查看官网的介绍:https://jwt.io/introduction/ jwt请求流程 引用官网的图片 中文介绍: 用户使用账号和面发出post请求: 服务器使用私钥创建一个jwt: 服务器返

  • Shiro + JWT + SpringBoot应用示例代码详解

    1.Shiro的简介 Apache Shiro是一种功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理,可用于保护 从命令行应用程序,移动应用程序到Web和企业应用程序等应用的安全. Authentication 身份认证/登录,验证用户是不是拥有相应的身份: Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限:即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色.或者细粒度的验证某个用户对某个资源是否具有某个权限: Crypto

  • springboot多数据源配置及切换的示例代码详解

    注:本文的多数据源配置及切换的实现方法是,在框架中封装,具体项目中配置及使用,也适用于多模块项目 配置文件数据源读取 通过springboot的Envioment和Binder对象进行读取,无需手动声明DataSource的Bean yml数据源配置格式如下: spring: datasource: master: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url:

  • Laravel 5.5 的自定义验证对象/类示例代码详解

    Laravel 5.5 将提供一个全新的自定义验证规则的对象,以作为原来的 Validator::extend 方法的替代. Laravel 5.5 将提供一个全新的自定义验证规则的对象,以作为原来的 Validator::extend 方法的替代..很多时候我们会直接用正则表达式来处理这种特殊的验证,也有时候我们会选择用 Validator::extend 来扩展一个自定义的规则.但在 Laravel 5.5 版本中,我们有了新的手段,只要定义一个实现 Illuminate\Contracts

  • Java的静态类型检查示例代码详解

    关于静态类型检查和动态类型检查的解释: 静态类型检查:基于程序的源代码来验证类型安全的过程: 动态类型检查:在程序运行期间验证类型安全的过程: Java使用静态类型检查在编译期间分析程序,确保没有类型错误.基本的思想是不要让类型错误在运行期间发生. 在各色各样的编程语言中,总共存在着两个类型检查机制:静态类型检查和动态类型检查. 静态类型检查是指通过对应用程序的源码进行分析,在编译期间就保证程序的类型安全. 动态类型检查是在程序的运行过程中,验证程序的类型安全.在Java中,编译期间使用静态类型

  • Vue创建头部组件示例代码详解

    Vue.js 组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 具体代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title

  • Java 添加Word目录的2种方法示例代码详解

    目录是一种能够快速.有效地帮助读者了解文档或书籍主要内容的方式.在Word中,插入目录首先需要设置相应段落的大纲级别,根据大纲级别来生成目录表.本文中生成目录分2种情况来进行: 1.文档没有设置大纲级别,生成目录前需要手动设置 2.文档已设置大纲级别,通过域代码生成目录 使用工具: •Free Spire.Doc for Java 2.0.0 (免费版) •IntelliJ IDEA 工具获取途径1:通过官网下载jar文件包,解压并导入jar文件到IDEA程序. 工具获取途径2:通过Maven仓

  • vue2.0自定义指令示例代码详解

    1.什么是指令? 指令通常以"v-"作为前缀, 以方便Vue知道你在使用一种特殊的标记. 除了 Vue 核心携带着的一些默认指令(v-model 和 v-show)之外, Vue 还允许你注册自己的自定义指令.某些情况下,还是需要对普通元素进行一些底层 DOM 访问, 这也是自定义指令仍然有其使用场景之处. 2.全局指令: 当页面加载时,元素将获取焦点,事实上,在访问页面时,如果你还没有点击任何地方,上面的输入框现在应该处于获取焦点的状态.现在让我们构建指令以完成此效果: <te

  • Go语言中strings和strconv包示例代码详解

    前缀和后缀 HasPrefix判断字符串s是否以prefix开头: strings.HaxPrefix(s string, prefix string) bool 示例: package main import ( "fmt" "strings" ) func main() { pre := "Thi" str1 := "This is a Go program!" fmt.Println(strings.HasPrefix(

  • django在保存图像的同时压缩图像示例代码详解

    假设我们有一个非常简单的Post模型,它将是一个图像及其描述, from django.db import models class Post(models.Model): text = models.TextField() image = models.ImageField(upload_to='images/') 但是我们要优化图像大小,这将由我们Post的image字段指出. 这样做有充分的理由-它有助于更快地加载网站/应用程序并减少我们的服务器存储. 在使用Django之前,首先让我们简

  • 封装一下vue中的axios示例代码详解

    在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中.他有很多优秀的特性,例如拦截请求和响应.取消请求.转换json.客户端防御cSRF等.所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库.如果还对axios不了解的,可以移步axios文档. 安装 npm install axios; // 安装axios 好了,下面开始今天的正文. 此次封装用以解决: (

随机推荐