SpringBoot2.0+Shiro+JWT 整合

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。 我们利用一定的编码生成 Token,并在 Token 中加入一些非敏感信息,将其传递。

安装环境

开发工具:STS
Maven版本:apache-maven-3.5.2
java jdk 1.8
MySQL版本:5.7
系统:Windows10

一.新建Maven项目

配置Maven项目的pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent> <groupId>cn.codepeople</groupId>
<artifactId>SpringBoot-Shiro-JWT</artifactId>
<version>0.0.1-SNAPSHOT</version> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<log4j.version>1.2.17</log4j.version>
<shiro.version>1.4.0</shiro.version>
<com.alibaba.druid.version>1.1.10</com.alibaba.druid.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.version>2.0.0</mybatis.version>
<jwt.version>3.8.0</jwt.version>
<swagger2.version>2.8.0</swagger2.version>
</properties> <dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 引入shiro的spring整合框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${com.alibaba.druid.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 打包时拷贝MyBatis的映射文件 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

二.准备shiro和jwt工具和相关类

JWTFilter.java

/**
* @Title: JWTFilter.java
* @Package www.codepeople.cn.shiro
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:52:19
* @version V1.0
*/ package www.codepeople.cn.shiro; import java.io.IOException; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod; import lombok.extern.slf4j.Slf4j; /**
* @ClassName: JWTFilter
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:52:19
*/
@Slf4j
public class JWTFilter extends BasicHttpAuthenticationFilter{ /**
* 判断用户是否想要登入。
* 检测header里面是否包含Authorization字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authorization");
log.info("判断用户是否想要登录:{}",authorization);
return authorization != null;
} /**
*
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader("Authorization");
log.info("判断用户是否想要登录x:{}",authorization);
JWTToken token = new JWTToken(authorization);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
} /**
* 这里我们详细说明下为什么最终返回的都是true,即允许访问
* 例如我们提供一个地址 GET /article
* 登入用户和游客看到的内容是不同的
* 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西
* 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
* 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可
* 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
} catch (Exception e) {
response401(request, response);
}
}
return true;
} /**
* 对跨域提供支持
*/
@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);
} /**
* 将非法请求跳转到 /401
*/
private void response401(ServletRequest req, ServletResponse resp) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
httpServletResponse.sendRedirect("/401");
} catch (IOException e) {
log.error(e.getMessage());
}
}
}

JWTToken.java

/**
* @Title: JWTToken.java
* @Package www.codepeople.cn.shiro
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:49:43
* @version V1.0
*/ package www.codepeople.cn.shiro; import org.apache.shiro.authc.AuthenticationToken; /**
* @ClassName: JWTToken
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:49:43
*/ public class JWTToken implements AuthenticationToken { private static final long serialVersionUID = 1L;
// 秘钥
private String token; public JWTToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
} @Override
public Object getCredentials() {
return token;
} }

MyRealm.java

/**
* @Title: MyRealm.java
* @Package www.codepeople.cn.shiro
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:31:33
* @version V1.0
*/ package www.codepeople.cn.shiro; import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired; import www.codepeople.cn.entity.User;
import www.codepeople.cn.service.UserService;
import www.codepeople.cn.util.JWTUtil; /**
* @ClassName: MyRealm
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:31:33
*/ public class MyRealm extends AuthorizingRealm { @Autowired
private UserService userService; @Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = JWTUtil.getUsername(principals.toString());
User user = userService.getUser(username);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole(user.getRole());
Set<String> permission = new HashSet<String>(Arrays.asList(user.getPerms().split(",")));
simpleAuthorizationInfo.addStringPermissions(permission); return simpleAuthorizationInfo;
} /**
* 默认使用此方法进行用户正确与否验证,错误抛出异常即可
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String token = (String) authenticationToken.getCredentials();
// 解密获得username,用于和数据库进行对比
String username = JWTUtil.getUsername(token);
if (username == null) {
throw new AuthenticationException("token 无效!");
} User user = userService.getUser(username);
if (user == null) {
throw new AuthenticationException("用户"+username+"不存在") ;
} if (!JWTUtil.verify(token, username, user.getPassword())) {
throw new AuthenticationException("账户密码错误!");
}
return new SimpleAuthenticationInfo(token, token, "my_realm");
} }

ShiroConfig.java

/**
* @Title: ShiroConfig.java
* @Package www.codepeople.cn.shiro
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:14:32
* @version V1.0
*/ package www.codepeople.cn.shiro; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.mgt.DefaultSecurityManager;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; /**
* @ClassName: ShiroConfig
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:14:32
*/
@Configuration
public class ShiroConfig { @Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("jwt", new JWTFilter());
shiroFilterFactoryBean.setFilters(filterMap);
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setUnauthorizedUrl("/401");
/**
* 自定义url规则
* http://shiro.apache.org/web.html#urls-
*/
Map<String, String> filterRuleMap = new LinkedHashMap<String, String>();
// 所有的请求通过我们自己的JWT filter
filterRuleMap.put("/**", "jwt");
// 访问401和404页面不通过我们的Filter
filterRuleMap.put("/401", "anon");
filterRuleMap.put("/404", "anon"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);
return shiroFilterFactoryBean;
} @Bean(name = "securityManager")
public DefaultSecurityManager getDefaultSecurityManager(@Qualifier("myRelam") MyRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
} @Bean(name="myRelam")
public MyRealm getMyRealm() {
return new MyRealm();
} /**
* 下面的代码是添加注解支持
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
// https://zhuanlan.zhihu.com/p/29161098
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
} @Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} @Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}

JWTUtil.java

/**
* @Title: JWTUtil.java
* @Package www.codepeople.cn.util
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:32:56
* @version V1.0
*/ package www.codepeople.cn.util; import java.io.UnsupportedEncodingException;
import java.util.Date; import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT; /**
* @ClassName: JWTUtil
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:32:56
*/ public class JWTUtil {
// 过期时间24小时
private static final long EXPRIE_TIME = 24*60*60*1000; public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC512(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
verifier.verify(token);
return true;
} catch (Exception e) {
return false;
}
}
/**
* @Title: getUsername
* @Description: 获取token中的信息无需secret解密也能获得
* @Author 刘仁
* @DateTime 2019年4月1日 下午4:42:39
* @param token
* @return
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").toString();
} catch (JWTDecodeException e) {
return null;
}
} public static String sign(String username, String secret) throws UnsupportedEncodingException {
Date date = new Date(System.currentTimeMillis()+EXPRIE_TIME);
Algorithm algorithm = Algorithm.HMAC512(secret);
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
}
}

登录功能实现

UserController.java

/**
* @Title: UserController.java
* @Package www.codepeople.cn.controller
* @Description:
* Copyright: Copyright (c) 2019 www.codepeople.cn Inc. All rights reserved.
* Website: www.codepeople.cn
* @Author 刘仁
* @DateTime 2019年4月1日 下午5:29:03
* @version V1.0
*/ package www.codepeople.cn.controller; import java.io.UnsupportedEncodingException;
import java.util.List; import javax.servlet.http.HttpServletResponse; import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import www.codepeople.cn.entity.ResponseBean;
import www.codepeople.cn.entity.User;
import www.codepeople.cn.service.UserService;
import www.codepeople.cn.util.JWTUtil; /**
* @ClassName: UserController
* @Description:
* @Author 刘仁
* @DateTime 2019年4月1日 下午5:29:03
*/
@RestController
public class UserController { @Autowired
private UserService userService; @PostMapping("/login")
@ApiOperation(value="登录功能", notes="用户密码登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String"),
@ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String")
})
public ResponseBean login(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpServletResponse response) throws UnsupportedEncodingException {
User user = userService.getUser(username);
if (user.getPassword().equals(password)) {
String token = JWTUtil.sign(username, password);
response.setHeader("token", token);
return new ResponseBean(200, "登录成功", token);
} else {
throw new UnauthorizedException();
}
} @GetMapping("/listUser")
@ApiOperation(value="获取用户列表", notes="获取用户列表")
public ResponseBean listUser() throws UnsupportedEncodingException {
List<User> listUser = userService.listUser();
return new ResponseBean(200, "用户列表", listUser);
} @RequestMapping("/401")
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ResponseBean unauthorized() {
return new ResponseBean(401, "未授权", null);
}
}

为了方便调试加入了Swagger的架包

整体的代码下载地址:https://gitee.com/VCS/springboot-shiro-jwt-demo

==================================================================

博客地址https://www.codepeople.cn

==================================================================

SpringBoot2.0+Shiro+JWT 整合的更多相关文章

  1. Spring Boot 鉴权之—— springboot2.0.4+mybatis 整合的完整用例

    自上一篇文章的基础上,Spring Boot 鉴权之—— JWT 鉴权我做了一波springboot2.0.4+mybatis 的整合. 参考文章: Spring Boot+Spring Securi ...

  2. SpringBoot2.0之四 简单整合MyBatis

    从最开始的SSH(Struts+Spring+Hibernate),到后来的SMM(SpringMVC+Spring+MyBatis),到目前的S(SpringBoot),随着框架的不断更新换代,也为 ...

  3. springboot2+shiro+jwt整合

    参考:https://www.jianshu.com/p/ef0a82d471d2 https://www.jianshu.com/p/3c51832f1051 https://blog.csdn.n ...

  4. spring boot Shiro JWT整合

    一个api要支持H5, PC和APP三个前端,如果使用session的话对app不是很友好,而且session有跨域攻击的问题,所以选择了JWT 1.导入依赖包 <dependency> ...

  5. SpringBoot2.0之五 优雅整合SpringBoot2.0+MyBatis+druid+PageHelper

    上篇文章我们介绍了SpringBoot和MyBatis的整合,可以说非常简单快捷的就搭建了一个web项目,但是在一个真正的企业级项目中,可能我们还需要更多的更加完善的框架才能开始真正的开发,比如连接池 ...

  6. Spring-boot2.0.1.BUILD-SNAPSHOT整合Elasticsearch报failed to load elasticsearch nodes错误解决办法

    spring-boot整合es的application.properties的默认配置为: spring.data.elasticsearch.cluster-nodes=localhost:9200 ...

  7. SpringBoot2.0之三 优雅整合Spring Data JPA

      在我们的实际开发的过程中,无论多复杂的业务逻辑到达持久层都回归到了"增删改查"的基本操作,可能会存在关联多张表的复杂sql,但是对于单表的"增删改查"也是不 ...

  8. springboot2.0和Druid整合配置数据源

    1. idea使用spring 初始化工具初始化springboot项目(要选中web) 下一步,下一步 2. 在pom.xml中,引入Druid连接池依赖: <dependency> & ...

  9. SpringBoot2.0针对请求参数@RequestBody验证统一拦截

    title: "SpringBoot2.0针对请求参数@RequestBody验证的统一拦截"categories: SpringBoot2.0 Shirotags: Spring ...

随机推荐

  1. loadrunner之运行方式:线程还是进程?

    loadrunner controller将使用驱动程序mmdrv运行Vuser. 用户可以在controller的run-time setting中选择Vuser的运行方式: 是多进程方式还是多线程 ...

  2. 关于thinkphp3自动完成的笔记

    当我在前台传入的主键id与字段表的主键id值时,在更新时tp总是判断为新增的状态(解决办法:将前台的表单主键名保持和数据表主键id名一只,手动创建数据) create时是先获取主键id判断'$type ...

  3. vue源码的构建

    一.vue构建的基本了解 1,开始学习vue的源码的学习,vue.js是基于rollup构建的 它的配置在 scripts下面 rollup是webpack的简小版针对于js进行压缩的,没有提供复杂的 ...

  4. c#一步一步实现ORM

    本篇适合新手了解学习orm.欢迎指正,交流学习. 现有的优秀的orm有很多. EF:特点是高度自动化,缺点是有点重. Nhibnate:缺点是要写很多的配置. drapper:最快的orm.但是自动化 ...

  5. 数据结构之二叉搜索树、AVL自平衡树

    前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神 ...

  6. Rendering React components to the document body

    React一个比较好用的功能是其简单的API,一个组件可以简单到一个return了组件结构的render函数.除了一个简单的函数之外,我们还有了一段有用且可复用的代码片段. 问题 不过有时候可能会受到 ...

  7. P3812 【模板】线性基

    P3812 [模板]线性基 理解 :线性基 类似于 向量的极大无关组,就是保持原来所有数的异或值的最小集合, 求解过程也类似,可以 O( 60 * n )的复杂度求出线性基,线性基有许多性质,例如 线 ...

  8. 56.两数之和.md

    描述 给一个整数数组,找到两个数使得他们的和等于一个给定的数 target. 你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标.注意这里下标的范围是 0 到 n-1. ...

  9. 关于java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to 实体类

    由于业务逻辑的复杂,有些sql语句写法hql不支持,所以hibernate查询直接用了sql原生查询,由于数据结果根据四个表查询出来,所以无法使用方法.addEntity(XXXXXXX.class) ...

  10. 如何去掉linux配置文件的注释行和空行

    1.使用grep -v "^#"  来去掉注释行,其中:-v  就是取相反的   ^# 表示以#开头的行 eg. grep -v "^#" /etc/vsftp ...