SpringDataJPA

(一)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(1)项目架构 (二)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(2)多个第三方服务端接入之云旺 IM (三)Java-解决实现 JPA 的 hibernate 自动建表的编码问题 (四)WEB 后台--基于 Token 的 Web 后台登录认证机制(并讲解其他认证机制以及 cookie 和 session 机制) (一)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(1)项目架构 文章结构:(1)项目环境搭建;(2)简单业务逻辑测试框架;(3)项目配置注意点;(4)快速开发上手技巧; 一

本文包含相关资料包-----> 点击直达获取<-------

(一)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(1)项目架构

(二)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(2)多个第三方服务端接入之云旺 IM

(三)Java-解决实现 JPA 的 hibernate 自动建表的编码问题

(四)WEB 后台--基于 Token 的 Web 后台登录认证机制(并讲解其他认证机制以及 cookie 和 session 机制)

(一)J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(1)项目架构

文章结构:(1)项目环境搭建;(2)简单业务逻辑测试框架;(3)项目配置注意点;(4)快速开发上手技巧;

一、项目环境搭建:

项目框架:Spring Data JPA+Spring+SpringMVC+Maven+JDK1.7+Tomcat7.0
使用 IDEA 开发

(1)Maven 构建:

```xml 4.0.0 com.fuzhu springdatajpa war 1.0-SNAPSHOT springdatajpa Maven Webapp http://maven.apache.org

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
<buildNumber>${maven.build.timestamp}</buildNumber>

<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.7</maven.compiler.source>

<java-version>1.7</java-version>

<version-springframework>4.0.4.RELEASE</version-springframework>
<version-slf4j>1.6.6</version-slf4j>
<version-hibernate.validator>5.0.1.Final</version-hibernate.validator>
<version-hibernate.jpa>1.0.1.Final</version-hibernate.jpa>
<version-hibernate>4.2.6.Final</version-hibernate>
<mysql.version>5.1.35</mysql.version>

org.springframework spring-web ${version-springframework} org.springframework spring-webmvc ${version-springframework} org.springframework spring-expression ${version-springframework} org.springframework spring-core ${version-springframework} org.springframework spring-context ${version-springframework} commons-logging commons-logging

<!-- spring-context 的扩展支持,用于 MVC 方面 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>${version-springframework}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>${version-springframework}</version>
</dependency>
<!-- springIoC(依赖注入)的基础实现 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>${version-springframework}</version>
</dependency>
<!-- Spring事务支持 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>${version-springframework}</version>
</dependency>
<!-- spring 对Junit 等测试框架的简单封装 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>${version-springframework}</version>
</dependency>
<!--hibernate的包-->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>${version-hibernate}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>${version-hibernate}</version>
  <exclusions>
    <exclusion>
      <artifactId>cglib</artifactId>
      <groupId>cglib</groupId>
    </exclusion>
    <exclusion>
      <artifactId>dom4j</artifactId>
      <groupId>dom4j</groupId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>${version-hibernate.validator}</version>
</dependency>
<dependency>
  <groupId>org.hibernate.javax.persistence</groupId>
  <artifactId>hibernate-jpa-2.0-api</artifactId>
  <version>${version-hibernate.jpa}</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-ehcache</artifactId>
  <version>${version-hibernate}</version>
</dependency>

<!-- Spring data jpa -->
<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-jpa</artifactId>
  <version>1.6.2.RELEASE</version>
</dependency>

<!-- AspectJ -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.6.10</version>
</dependency>

<!-- Logging -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>${version-slf4j}</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
  <version>${version-slf4j}</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
<!-- logger end-->

<!-- slf4j -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.7</version>
</dependency>
<!--slf4j-log4j12 -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.12</version>
</dependency>

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>3.0-alpha-1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.2</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

<!-- druid dataSource -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.0.0</version>
</dependency>

<!--mysql -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>${mysql.version}</version>
</dependency>

<!-- Apache Commons Dependencies -->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.3.2</version>
</dependency>

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-digester3</artifactId>
  <version>3.2</version>
</dependency>

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.4</version>
</dependency>

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-exec</artifactId>
  <version>1.2</version>
</dependency>

<dependency>
  <groupId>commons-beanutils</groupId>
  <artifactId>commons-beanutils</artifactId>
  <version>1.9.1</version>
</dependency>

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.9</version>
</dependency>
<!--common end-->
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2.2</version>
</dependency>

<!--fastjson-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.5</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.1.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.1.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.1.0</version>
</dependency>

<!-- junit测试的包-->
<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-core</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-library</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

springdatajpa org.apache.maven.plugins maven-compiler-plugin 1.7 1.7 ```

(2).spring 配置文件:

```xml

<!--扫描资源文件-->
<context:property-placeholder location="classpath:database.properties" ignore-unresolvable="true"/>
<!--Druid数据源-->
<bean id="dataSource" init-method="init" destroy-method="close">
  <property name="driverClassName" value="${database.driverClassName}"/>
  <property name="url" value="${database.url}" />
  <property name="username" value="${database.username}" />
  <property name="password" value="${database.password}" />
  <!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
  <property name="testOnBorrow" value="false" />
  <property name="testOnReturn" value="false" />
  <!-- 申请连接的时候检测 -->
  <property name="testWhileIdle" value="false" />
  <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  <property name="timeBetweenEvictionRunsMillis" value="1800000" />
  <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  <property name="minEvictableIdleTimeMillis" value="1800000" />
  <property name="defaultAutoCommit" value="false" />

</bean>


<!-- 指定事务管理器,JPA使用JpaTransactionManager事务管理器实现. -->
<bean id="transactionManager" >
  <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<!-- 指定JPA实现 -->
<bean id="jpaVendorAdapter"
  >
  <property name="showSql" value="false" />
</bean>

<!-- 适用于所有环境的FactoryBean,能全面控制EntityManagerFactory配置,如指定Spring定义的DataSource等等. -->
<bean id="entityManagerFactory"
  >
  <!-- 指定持久化单元名字,即JPA配置文件中指定的 -->
  <property name="persistenceUnitName" value="com.ima" />
  <!-- 见上 -->
  <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
  <!-- 见上 -->
  <property name="dataSource" ref="dataSource" />
</bean>
<jpa:repositories base-package="com.ima.repository" />

<!-- 开启事务管理注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

```

(3).springMVC 的配置:

```xml

<!-- 开启SpringMVC注解 -->
<mvc:annotation-driven />

<!-- 只扫描Controller 注解 -->
<context:component-scan base-package="com.ima.controller" use-default-filters="false">
  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
  <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>


<!-- 定义视图解析器 -->
<bean id="resourceViewResolver" >
  <property name="prefix" value="/WEB-INF/views/"/>
  <property name="suffix" value=".jsp"/>
  <property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
<!-- 用于返回json格式 -->
<bean id="mappingJacksonHttpMessageConverter" >
  <property name="supportedMediaTypes">
    <list>
      <value>application/x-www-form-urlencoded;charset=UTF-8</value>
    </list>
  </property>
</bean>

<!--上传文件配置-->
<bean id="multipartResolver" >
  <property name="defaultEncoding" value="utf-8"/>
  <property name="maxUploadSize" value="10485760"/>
  <property name="maxInMemorySize" value="40960"/>
</bean>


<mvc:annotation-driven>
  <mvc:message-converters register-defaults="true">
    <!--字符串编码转换-->
    <bean id="stringHttpMessageConverter" >
      <constructor-arg value="UTF-8"/>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

```

(4).数据源的配置:

xml database.url=jdbc:mysql://127.0.0.1:3306/aidou?useUnicode=true&characterEncoding=utf8 database.username=root database.password=751197996 database.driverClassName=com.mysql.jdbc.Driver

(5).log4j 的日志配置(见源码) (6).JPA 的配置包,指定持久化单元:

```xml

    <!--ENABLE_SELECTIVE[(默认和推荐值):实体不缓存,除非明确标记为可缓存的。] DISABLE_SELECTIVE[实体缓存,除非明确标记为不缓存] NONE[无缓存] ALL[全部缓存]-->
    <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
    <properties>
        <!-- 使用MySQL方言 -->
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>

        <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
        <property name="hibernate.connection.url" value="jdbc:mysql:///aidou" />
        <property name="hibernate.connection.username" value="root" />
        <property name="hibernate.connection.password" value="751197996" />

        <property name="hibernate.connection.charSet" value="UTF-8" />
        <!-- 自动输出schema创建DDL语句 -->
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <!-- 显示SQL语句 -->
        <property name="hibernate.show_sql" value="true" />
        <!-- 在显示SQL语句时格式化语句 -->
        <property name="hibernate.format_sql" value="true" />
        <property name="hibernate.use_sql_comments" value="true" />

         <!--org.hibernate.cache.EhCacheRegionFactory -->
        <!-- Enable Batching -->
        <property name="hibernate.jdbc.batch_size" value="20" />
        <property name="hibernate.default_batch_fetch_size" value="10" />

        <!-- Hibernate二级缓存相关配置 -->
        <!-- 开启二级缓存 -->
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <!-- 打开Hibernate查询缓存 -->
        <property name="hibernate.cache.use_query_cache" value="true" />
        <!-- 配置缓存提供者 -->
        <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
        <property name="hibernate.generate_statistics" value="true" />

    </properties>
</persistence-unit>

```

(7).web.xml 配置

```xml

Archetype Created Web Application

contextConfigLocation classpath:META-INF/spring/spring-jpa.xml org.springframework.web.context.ContextLoaderListener

log4jConfigLocation classpath:META-INF/log4j.properties org.springframework.web.util.Log4jConfigListener

Set Character Encoding org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true Set Character Encoding /*

appServlet org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:META-INF/springmvc/spring-mvc.xml 1 appServlet /

Spring OpenEntityManagerInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter entityManagerFactoryBeanName entityManagerFactory Spring OpenEntityManagerInViewFilter /*

DruidWebStatFilter com.alibaba.druid.support.http.WebStatFilter exclusions .js, .gif, .jpg, .png, .css, .ico,/druid/ sessionStatMaxCount 1000 sessionStatEnable false DruidWebStatFilter /

DruidStatView com.alibaba.druid.support.http.StatViewServlet resetEnable true loginUsername druid loginPassword druid DruidStatView /druid/* ```

这样就完成了工程的配置了!!!

二、简单业务逻辑测试框架

简单的 bean:(为了偷懒就用 JPA 自动建表了,不过后面会给出 SQL,而且 JPA 自动建表有坑!!)

```java //积分变化记录 @Entity @Table(name = "i_dou_change") public class IDouChange { private Long id; //积分变化的类型,拉黑或者在线或者充值 private String changeType; private Date createTime; //积分变化数 private Integer iDouCount; //积分变化者 private User user;

public IDouChange() {
}

@Id
@GeneratedValue
public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}


@Column(name = "change_type")
public String getChangeType() {
    return changeType;
}

public void setChangeType(String changeType) {
    this.changeType = changeType;
}

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time",nullable=true,columnDefinition="timestamp default current_timestamp")
public Date getCreateTime() {
    return createTime;
}

public void setCreateTime(Date createTime) {
    this.createTime = createTime;
}

@Column(name = "i_dou_count")
public Integer getiDouCount() {
    return iDouCount;
}

public void setiDouCount(Integer iDouCount) {
    this.iDouCount = iDouCount;
}

//简单的多对一 @JoinColumn(name="user_id") @ManyToOne(fetch= FetchType.LAZY) public User getUser() { return user; }

public void setUser(User user) {
    this.user = user;
}

} ```

对应的 repository 接口 dao

```java @Repository public interface AiDouChangeRepository extends JpaRepository {

} ```

对应的 service

```java @Service public class AiDouService { @Autowired private AiDouChangeRepository aiDouChangeRepository; public void save(IDouChange iDouChange){ aiDouChangeRepository.save(iDouChange); }

} ```

写个测试的 controller

```java @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Autowired private AiDouService aiDouService;

//test:
@RequestMapping(value = "/test", method = {RequestMethod.GET, RequestMethod.POST}, produces = "text/html;charset=UTF-8")
//测试嘛
public String test(String type, Integer iDouCount )  {
    DTO dto = new DTO();
    IDouChange iDouChange = new IDouChange();
    iDouChange.setChangeType(type);
    iDouChange.setiDouCount(iDouCount);
    aiDouService.save(iDouChange);
    if (iDouChange == null) {
        dto.code = "-1";
        dto.msg = "Have bean registered";
        return JSON.toJSONString(dto);
    } else {
        return JSON.toJSONString(iDouChange);
    }
}

} ```

然后??直接部署呀!!对应传参,测试成功咯!!!

一对多没实现关联,傻逼的插入就是这样的啦

三、项目配置注意点:

还有就是在 web.xml 中映射好 Spring 文件以及 SpringMVC 文件

四、快速开发上手技巧:

(1)在一般项目时,使用这个框架 Spring Data JPA+Spring+SpringMVC+Maven,快速映射 dao 层即可编写业务 (2)接口文档工具,swagger+postman,让前端移动端更快速接入 (3)数据库最好还是新建好导入,再用 IDEA 的 persistence 导入,这样的话,编码问题就不会出现太多。用 IDEA 的 persistence 导入姿势 https://link.csdn.net/?target=https%3A%2F%2Fmy.oschina.net%2Fgaussik%2Fblog%2F513444 (4)虽然不建议用 jpa 自动建表,因为有编码问题,不懂的人,就尴尬了,要搞很久,这个系列的第三篇博客会讲解怎么解决这个的编码问题。

J2EE 项目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven 快速开发(2)多个第三方服务端接入之云旺 IM

文章结构:(1)IM 的介绍、第三方 IM 的推荐以及服务端接入配合 App 的大概流程;(2)云旺 IM 快速接入。

一、IM 的介绍以及服务端接入方案:

即时通信(IM,即 Instant Messaging)是指能够即时发送和接收互联网消息等的业务。 (1)服务端接入 IM 是为了干什么呢?? 1.第三方的 IM 不想知道你的应用关于用户的一些东西,比如你的用户关系,你的用户关联算法 2.账号密码的管理,还有各项信息的返回给客户端,比如聊天记录导出, 3.减轻后台的压力,一般小公司是不可能自己做一套撑住并发的 IM 方案的,使用第三方利于减轻压力。 (2)第三方 IM 的推荐 1.选择考虑方向 稳定性 安全性 功能性 费用 运维服务 企业规模 升级策略 SDK 支持 服务 通信协议 2.第三方公司背景及价格: 创业公司:融云、环信、yun2win、容联 大企业:网易云信(专业、价格高)、阿里云旺(特殊,原阿里悟空保留下来的,价格不算高) 融云:优点:费用低、功能可满足 。缺点:暂时无。 网易云信: 优点:专业、功能多、稳定、安全。缺点:费用高 阿里云旺:优点:专业、功能多、稳定、安全。缺点:没接入阿里云,而是在淘宝的 API 上,不清楚未来。 (3)一般小型 app 的方案: 1.单纯地为用户注册 IM 账号密码 2.注册账号密码 + 客服 剩下的就是一些 IM 给的一些小接口功能了。

二、云旺 IM 快速接入:

(1)当然是先申请为开发者,申请功能,创建个应用拿到 appkey 啦: 指引在此 https://link.csdn.net/?target=http%3A%2F%2Fbaichuan.taobao.com%2Fdoc2%2Fdetail%3Fspm%3Da3c0d.7629140.0.0.ziGjic%26treeId%3D28%26articleId%3D102565%26docType%3D1 (2)下载 SDK,拿到两个 jar 包,放入自己的工程: 指引在此 https://link.csdn.net/?target=http%3A%2F%2Fbaichuan.taobao.com%2Fdoc2%2Fdetail%3Fspm%3Da3c0d.7629140.0.0.0466Lq%26treeId%3D28%26articleId%3D102556%26docType%3D1%23s2 (3)必须学会用他官方提供的接口工具,试着玩下先: 指引在此 https://link.csdn.net/?target=http%3A%2F%2Fopen.taobao.com%2Fapitools%2FapiTools.htm%3Fspm%3Da3c0d.7629140.0.0.0466Lq%26catId%3D20654%26apiId%3D24164%26apiName%3Dtaobao.openim.users.add%26scopeId%3D 怎么使用??例子先选个添加用户账号到我们开发者的 IM 账号先。 清楚添加用户的操作以及返回体先 https://link.csdn.net/?target=http%3A%2F%2Fopen.taobao.com%2Fdoc2%2FapiDetail.htm%3FapiId%3D24164

(4)在我们的工程中编写啦:按所给例子来 我们看到他的使用范例对吧??但是我们要对他的一些方法有清晰的认识。

java //例子是导入用户账号 //这两句是调用淘宝的相关API咯,支持restful风格的api /* * url就是第三方IM的入口咯。 * http请求地址:http://gw.api.taobao.com/router/rest * https请求地址:https://eco.taobao.com/router/rest * appkey和secret:这个应用的凭证信息,是调用API唯一凭证。 */ TaobaoClient client = new DefaultTaobaoClient(url, appkey, secret); OpenimUsersAddRequest req = new OpenimUsersAddRequest(); //存储用户的列表 List<Userinfos> list2 = new ArrayList<Userinfos>(); //这个是请求参数,就是云旺IM会帮我们的用户保存什么信息??这个就是一个用户咯 Userinfos obj3 = new Userinfos(); list2.add(obj3); obj3.setNick("king"); obj3.setIconUrl("http://xxx.com/xxx"); obj3.setTaobaoid("tbnick123"); obj3.setUserid("imuser123"); obj3.setPassword("xxxxxx"); obj3.setRemark("demo"); obj3.setExtra("{}"); obj3.setCareer("demo"); obj3.setVip("{}"); obj3.setAddress("demo"); obj3.setName("demo"); obj3.setAge(123L); obj3.setGender("M"); obj3.setWechat("demo"); obj3.setQq("demo"); obj3.setWeibo("demo"); req.setUserinfos(list2);//一会说明下这个set方法 OpenimUsersAddResponse rsp = client.execute(req);//提交整个事务给淘宝clientAPI System.out.println(rsp.getBody());//打印返回体

```java //刚刚所说的set方法,我们可以看到有两个形式 //这是是要你去拼接JSON数组 public void setUserinfos(String userinfos) { this.userinfos = userinfos; } //这个则是规定你要存进一个用户的列表 public void setUserinfos(List userinfos) { this.userinfos = (new JSONWriter(false, true)).write(userinfos); }

```

好了,这样就完成接入云旺 IM 的添加用户功能了,很简单吧??不懂的可以看下给出的 DEMO。

Java-解决实现 JPA 的 hibernate 自动建表的编码问题

hibernate 自动建表的编码应该是数据默认的编码格式 latin1_swedish_ci,所以我们直接自动建表,即使你给 persistenceUnitName 的持久单元设定了 utf8,还是使用 latin1 编码的。

因为什么呢??因为你使用的 hibernate 方言问题,对应 MySQL 的翻译的方言。 所以我们怎么做??

(1)继承 MySQL5InnoDBDialect 覆写方法:

像安卓的自定义控件一样,为了多样化的控件,我们需要自己去开发新控件,就是去使用继承的原理,覆写父类方法。

```java public class MySQL5DialectUTF8 extends MySQL5InnoDBDialect {

@Override
public String getTableTypeString() {
    return " ENGINE=InnoDB DEFAULT CHARSET=utf8";
}

} ```

(2)在持久化单元引用这个被覆写的方言:

```xml

        <property name="hibernate.dialect" value="com.ima.utils.MySQL5DialectUTF8"/>

```

Web 后台--基于 Token 的 Web 后台登录认证机制(并讲解其他认证机制以及 cookie 和 session 机制)

文章结构:

(1)JWT(一种基于 token 的认证方案)介绍并介绍其他几大认证机制; (2)cookie 和 session 机制; (3)Token 机制相对于 Cookie 机制的好处; (4)JWT 的 Java 实现;

一、JWT(一种基于 token 的认证方案)介绍:

(1)概述:

JWT 就是一种 Token 的编码算法,服务器端负责根据一个密码和算法生成 Token,然后发给客户端,客户端只负责后面每次请求都在 HTTP header 里面带上这个 Token,服务器负责验证这个 Token 是不是合法的,有没有过期等,并可以解析出 subject 和 claim 里面的数据。

(2)相关问题:

1.为什么用 JWT?

JWT 只通过算法实现对 Token 合法性的验证,不依赖数据库,Memcached 的等存储系统,因此可以做到跨服务器验证,只要密钥和算法相同,不同服务器程序生成的 Token 可以互相验证。

2.JWT Token 不需要持久化在任何 NoSQL 中,不然背离其算法验证的初心
3.在退出登录时怎样实现 JWT Token 失效呢?

退出登录, 只要客户端端把 Token 丢弃就可以了,服务器端不需要废弃 Token。

4.怎样保持客户端长时间保持登录状态?

服务器端提供刷新 Token 的接口, 客户端负责按一定的逻辑刷新服务器 Token。

5.服务器端是否应该从 JWT 中取出 userid 用于业务查询?

REST API 是无状态的,意味着服务器端每次请求都是独立的,即不依赖以前请求的结果,因此也不应该依赖 JWT token 做业务查询, 应该在请求报文中单独加个 userid 字段。

为了做用户水平越权的检查,可以在业务层判断传入的 userid 和从 JWT token 中解析出的 userid 是否一致, 有些业务可能会允许查不同用户的数据。

(3)其他几大认证机制:

1.HTTP Basic Auth:

HTTP Basic Auth 简单点说明就是每次请求 API 时都提供用户的 username 和 password,简言之,Basic Auth 是配合 RESTFul API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的 RESTFul API 时,尽量避免采用 HTTP Basic Auth

2.OAuth(开放授权):

是一个开放的授权标准,允许用户让第三方应用访问该用户在某一 Web 服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

这种基于 OAuth 的认证机制适用于个人消费者类的互联网产品,如社交类 APP 等应用,但是不太适合拥有自有认证权限管理的企业应用;

3.Cookie Auth:

Cookie 认证机制就是为一次请求认证在服务端创建一个 Session 对象,同时在客户端的浏览器端创建了一个 Cookie 对象;通过客户端带上来 Cookie 对象来与服务器端的 session 对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie 会被删除。但可以通过修改 cookie 的 expire time 使 cookie 在一定时间内有效;

二、cookie 和 session 机制:

(1)概述:

Cookie 和 Session 是为了在无状态的 HTTP 协议之上维护会话状态,使得服务器可以知道当前是和哪个客户在打交道。

Session 是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;

Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现 Session 的一种方式。

因为 HTTP 协议是无状态的,即每次用户请求到达服务器时,HTTP 服务器并不知道这个用户是谁、是否登录过等。现在的服务器之所以知道我们是否已经登录,是因为服务器在登录时设置了浏览器的 Cookie!Session 则是借由 Cookie 而实现的更高层的服务器与浏览器之间的会话。

(2)cookie 实现机制:

Cookie 是由客户端保存的小型文本文件,其内容为一系列的键值对。 Cookie 是由 HTTP 服务器设置的,保存在浏览器中, 在用户访问其他页面时,会在 HTTP 请求中附上该服务器之前设置的 Cookie。

Cookie 的传递流程:

1.浏览器向某个 URL 发起 HTTP 请求(可以是任何请求,比如 GET 一个页面、POST 一个登录表单等)

2.对应的服务器收到该 HTTP 请求,并计算应当返回给浏览器的 HTTP 响应。(HTTP 响应包括请求头和请求体两部分)

3.在响应头加入 Set-Cookie 字段,它的值是要设置的 Cookie。

4.浏览器收到来自服务器的 HTTP 响应。

5.浏览器在响应头中发现 Set-Cookie 字段,就会将该字段的值保存在内存或者硬盘中。(Set-Cookie 字段的值可以是很多项 Cookie,每一项都可以指定过期时间 Expires。 默认的过期时间是用户关闭浏览器时。)

6.浏览器下次给该服务器发送 HTTP 请求时, 会将服务器设置的 Cookie 附加在 HTTP 请求的头字段 Cookie 中。(浏览器可以存储多个域名下的 Cookie,但只发送当前请求的域名曾经指定的 Cookie, 这个域名也可以在 Set-Cookie 字段中指定)。)

7.服务器收到这个 HTTP 请求,发现请求头中有 Cookie 字段, 便知道之前就和这个用户打过交道了.

8.过期的 Cookie 会被浏览器删除。

总之

服务器通过 Set-Cookie 响应头字段来指示浏览器保存 Cookie, 浏览器通过 Cookie 请求头字段来告诉服务器之前的状态。 Cookie 中包含若干个键值对,每个键值对可以设置过期时间。

Cookie 的安全隐患:
Cookie 提供了一种手段使得 HTTP 请求可以附加当前状态, 现今的网站也是靠 Cookie 来标识用户的登录状态的:

1.用户提交用户名和密码的表单,这通常是一个 POST HTTP 请求。

2.服务器验证用户名与密码,如果合法则返回 200(OK)并设置 Set-Cookie 为 authed=true。

3.浏览器存储该 Cookie。

4.浏览器发送请求时,设置 Cookie 字段为 authed=true。

5.服务器收到第二次请求,从 Cookie 字段得知该用户已经登录。 按照已登录用户的权限来处理此次请求。

问题是什么??风险是什么??

我们知道可以发送 HTTP 请求的不只是浏览器,很多 HTTP 客户端软件(包括 curl、Node.js)都可以发送任意的 HTTP 请求,可以设置任何头字段。 假如我们直接设置 Cookie 字段为 authed=true 并发送该 HTTP 请求, 服务器岂不是被欺骗了?这种攻击非常容易,Cookie 是可以被篡改的! Cookie 防篡改机制:

服务器可以为每个 Cookie 项生成签名,由于用户篡改 Cookie 后无法生成对应的签名, 服务器便可以得知用户对 Cookie 进行了篡改。

例子:一个简单的校验过程:

1.在服务器中配置一个不为人知的字符串(我们叫它 Secret),比如:x$sfz32。

2.当服务器需要设置 Cookie 时(比如 authed=false),不仅设置 authed 的值为 false, 在值的后面进一步设置一个签名,最终设置的 Cookie 是 authed=false|6hTiBl7lVpd1P。

3.签名 6hTiBl7lVpd1P 是这样生成的:Hash(‘x$sfz32’+‘true’)。 要设置的值与 Secret 相加再取哈希。

4.用户收到 HTTP 响应并发现头字段 Set-Cookie: authed=false|6hTiBl7lVpd1P。

5.用户在发送 HTTP 请求时,篡改了 authed 值,设置头字段 Cookie: authed=true|???。 因为用户不知道 Secret,无法生成签名,只能随便填一个。

6.服务器收到 HTTP 请求,发现 Cookie: authed=true|???。服务器开始进行校验: Hash(‘true’+‘x$sfz32’),便会发现用户提供的签名不正确。

通过给 Cookie 添加签名,使得服务器得以知道 Cookie 被篡改。但是!!还是有风险!

因为 Cookie 是明文传输的, 只要服务器设置过一次 authed=true|xxxx 我不就知道 true 的签名是 xxxx 了么, 以后就可以用这个签名来欺骗服务器了。因此 Cookie 中最好不要放敏感数据。 一般来讲 Cookie 中只会放一个 Session Id,而 Session 存储在服务器端。

(3)session 的实现机制:

1.概述:

Session 是存储在服务器端的,避免了在客户端 Cookie 中存储敏感数据。 Session 可以存储在 HTTP 服务器的内存中,也可以存在内存数据库(如 Redis)中, 对于重量级的应用甚至可以存储在数据库中。

例子:存储在 Redis 中的 Session 为例,考察如何验证用户登录状态的问题。

1.用户提交包含用户名和密码的表单,发送 HTTP 请求。

2.服务器验证用户发来的用户名密码。

3.如果正确则把当前用户名(通常是用户对象)存储到 Redis 中,并生成它在 Redis 中的 ID。

这个 ID 称为 Session ID,通过 Session ID 可以从 Redis 中取出对应的用户对象, 敏感数据(比如 authed=true)都存储在这个用户对象中。

4.设置 Cookie 为 sessionId=xxxxxx|checksum 并发送 HTTP 响应, 仍然为每一项 Cookie 都设置签名。

5.用户收到 HTTP 响应后,便看不到任何敏感数据了。在此后的请求中发送该 Cookie 给服务器。

6.服务器收到此后的 HTTP 请求后,发现 Cookie 中有 SessionID,进行放篡改验证。

7.如果通过了验证,根据该 ID 从 Redis 中取出对应的用户对象, 查看该对象的状态并继续执行业务逻辑。

实现上述过程,在 Web 应用中可以直接获得当前用户。 相当于在 HTTP 协议之上,通过 Cookie 实现了持久的会话。这个会话便称为 Session。

三、Token 认证机制相对于 Cookie 等机制的好处:

  1. 支持跨域访问:

Cookie 是不允许垮域访问的,这一点对 Token 机制是不存在的,前提是传输的用户认证信息通过 HTTP 头传输。(垮域访问:两个域名之间不能跨过域名来发送请求或者请求数据)

2.无状态(也称:服务端可扩展行):

Token 机制在服务端不需要存储 session 信息,因为 Token 自身包含了所有登录用户的信息,只需要在客户端的 cookie 或本地介质存储状态信息.

3.更适用 CDN:

可以通过内容分发网络请求你服务端的所有资料(如:JavaScript,HTML,图片等),而你的服务端只要提供 API 即可.

4.去耦:

不需要绑定到一个特定的身份验证方案。Token 可以在任何地方生成,只要在你的 API 被调用的时候,你可以进行 Token 生成调用即可.

5.更适用于移动应用:

当你的客户端是一个原生平台(iOS, Android,Windows 8 等)时,Cookie 是不被支持的(你需要通过 Cookie 容器进行处理),这时采用 Token 认证机制就会简单得多。 6. #### CSRF:

因为不再依赖于 Cookie,所以你就不需要考虑对 CSRF(跨站请求伪造)的防范。

7.性能:

一次网络往返时间(通过数据库查询 session 信息)总比做一次 HMACSHA256 计算 的 Token 验证和解析要费时得多.

8.不需要为登录页面做特殊处理:

如果你使用 Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.

9.基于标准化:

你的 API 可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).

四、JWT 的 Java 实现:

概述:一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。这里我们只使用简单的载荷,并将 JSON 对象进行 base64 编码得到 token
过程:登录为例子

第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交后到服务器的登录处理的 Action 层(controller)

Login Action 调用认证服务进行用户名密码认证,如果认证通过,Login Action 层调用用户信息服务获取用户信息(包括完整的用户信息及对应权限信息);

返回用户信息后,Login Action 从配置文件再经过工具类处理获取 Token 签名生成的秘钥信息,进行 Token 的生成

生成 Token 的过程中可以调用第三方的 JWT Lib 生成签名后的 JWT 数据;

完成 JWT 数据签名后,将其设置到 COOKIE 对象中,并重定向到首页,完成登录过程;

请求认证:

使用:基于 Token 的认证机制会在每一次请求中都带上完成签名的 Token 信息,这个 Token 信息可能在 COOKIE 中,也可能在 HTTP 的 Authorization 头中;

注意:

客户端(APP 客户端或浏览器)通过 GET 或 POST 请求访问资源(页面或调用 API);

认证服务作为一个 Middleware HOOK 对请求进行拦截,首先在 cookie 中查找 Token 信息,如果没有找到,则在 HTTP Authorization Head 中查找;

如果找到 Token 信息,则根据配置文件中的签名加密秘钥,调用 JWT Lib 对 Token 信息进行解密和解码;

完成解码并验证签名通过后,对 Token 中的 exp、nbf、aud 等信息进行验证;

全部通过后,根据获取的用户的角色权限信息,进行对请求的资源的权限逻辑判断;

如果权限逻辑判断通过则通过 Response 对象返回;否则则返回 HTTP 401;

(1)使用 JWT 的包:maven 导入

```xml

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.6.0</version>
</dependency>

```

(2)一个生成 token 的工具类:

```java public class JavaWebToken {

private static Logger log = Logger.getLogger(JavaWebToken.class);

private static Key getKeyInstance() {

// return MacProvider.generateKey(); //We will sign our JavaWebToken with our ApiKey secret SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("APP"); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); return signingKey; }

public static String createJavaWebToken(Map<String, Object> claims) {
    return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, getKeyInstance()).compact();
}

public static Map<String, Object> verifyJavaWebToken(String jwt) {
    try {

        Map<String, Object> jwtClaims =
                Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody();
        return jwtClaims;
    } catch (Exception e) {
        log.error("json web token verify failed");
        return null;
    }
}

} ```

(3)一个从 request 拿去 session,并且解密 session 得到 token 得到用户 id 的类

```java public class AuthUtil { private static Map getClientLoginInfo(HttpServletRequest request) throws Exception { Map r = new HashMap<>(); String sessionId = request.getHeader("sessionId"); if (sessionId != null) { r = decodeSession(sessionId); return r; } throw new Exception("session解析错误"); }

public static Long getUserId(HttpServletRequest request) throws Exception {
    return  Long.valueOf((Integer)getClientLoginInfo(request).get("userId"));

}

/**
 * session解密
 */
public static Map<String, Object> decodeSession(String sessionId) {
    try {
        return verifyJavaWebToken(sessionId);
    } catch (Exception e) {
        System.err.println("");
        return null;
    }
}

} ```

使用例子:

登录的时候把信息放进 session,存到 map 里,再交由 JWT 得到 token 保存起来

```java //登录 @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST}, produces = "text/html;charset=UTF-8") public String login(String account) { User user = userService.login(account);

    DTO dto = new DTO();
    if (user == null) {
        dto.code = "-1";
        dto.msg = "Have not registered";
    } else {
        //把用户登录信息放进Session
        Map<String, Object> loginInfo = new HashMap<>();
        loginInfo.put("userId", user.getId());
        String sessionId = JavaWebToken.createJavaWebToken(loginInfo);
        System.out.println("sessionID"+sessionId);
        dto.data = sessionId;
    }
    return JSON.toJSONString(dto);
}

```

用户登录以后,其他的用户性知道的操作就可以使用 token 进行了,安全快捷方便:

java //修改昵称 @RequestMapping(value = "/updateName", method = {RequestMethod.GET, RequestMethod.POST}) public String updateName(HttpServletRequest request, String name) { DTO dto = new DTO(); try { //从session拿到token,再解密得到userid Long userId = AuthUtil.getUserId(request); boolean userIsExist = userService.updateName(userId, name); if (userIsExist == false) { dto.code = "-1"; dto.msg = "Have not updateAvatar"; } } catch (Exception e) { e.printStackTrace(); } return JSON.toJSONString(dto); }

这就是 token 机制的登录认证功能简单实现了,安全机制等,以后会有博客补充。

参考文献

  • 基于亚马逊网络服务的优惠商品搜索发布系统(吉林大学·徐诗垚)
  • Spring轻量级框架在项目管理信息系统开发中的应用研究(西南交通大学·段培勇)
  • 面向中小企业的信息管理系统的设计与实现(北京邮电大学·贾士强)
  • 一个通用论坛系统的设计与实现(山东大学·张正)
  • 基于Struts2+Spring3+Hibernate3+Ajax的DRP系统(长安大学·申海杰)
  • J2EE环境下通用数据操作框架的研究(山东大学·杨媛媛)
  • 基于J2EE的企业电子商务平台的研究及优化设计(武汉理工大学·金双武)
  • 基于OAuth2.0协议的企业分布式授权系统设计与实现(华中科技大学·支猛)
  • JavaMail规范的研究和实现(四川大学·孙鹏)
  • 基于Spring框架的数据集成与转换研究(中国海洋大学·刘扬)
  • 基于.NET B/S结构的数据库系统研发(兰州大学·王小伟)
  • 基于OAuth2.0协议的企业分布式授权系统设计与实现(华中科技大学·支猛)
  • 基于Struts2+Spring3+Hibernate3+Ajax的DRP系统(长安大学·申海杰)
  • 亿贝数据搬运平台的设计与实现(南京大学·谈琪)
  • 齐齐哈尔合众商务科技有限公司门户网站设计(吉林大学·孟云飞)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:源码工厂 ,原文地址:https://m.bishedaima.com/yuanma/35693.html

相关推荐

发表回复

登录后才能评论