在这个demo中,整合了ssm框架,注册的时候会将密码进行加密,登录的时候会根据username和userid生成token,并编写了拦截器,对方法进行了拦截,除了登录和注册,其他接口必须掺入token才可以,通过前端调用接口时出现了跨域问题,在此也进行了解决
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.st</groupId>
<artifactId>study</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>studyMaven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.source.Encoding>UTF-8</project.build.source.Encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<!--MyBatis相关-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--MyBatis整合Spring的包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.0.4</version>
</dependency>
<!--数据库相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--Spring相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!--SpringMVC相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!--web相关-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</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>
<scope>runtime</scope>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--生成token-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Compile -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 打包跳过Test -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!--<path>/${project.artifactId}</path>-->
<path>/</path>
<port>80</port>
</configuration>
</plugin>
<!-- 资源文件拷贝插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<resources>
<!-- 使用Maven部署的时候,xml和properties配置文件也一起部署到Tomcat -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties
**/ *.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<!-- 默认是以下配置 -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties
**/ *.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- 跨域请求 -->
<filter>
<filter-name>SimpleCORSFilter</filter-name>
<filter-class>com.study.utils.SimpleCORSFilter</filter-class>
<init-param>
<param-name>IsCross</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SimpleCORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--1.支持restful的过滤器(如果需要就配置在所有过滤器之前)-->
<!-- 解决put和delete请求无法获取表单数据的问题 -->
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 将POST请求转化为DELETE或者是PUT 要用_method指定真正的请求参数 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--2.SpringMVC的DispatcherServlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Servlet的初始化参数,读取springmvc的配置文件,创建spring容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet启动时加载对象 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--3.乱码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置编码为UTF-8 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 过滤所有请求 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--4.Spring容器(要在服务器启动的时候就创建Spring容器)-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMybatis.xml</param-value>
</context-param>
</web-app>
jdbc.properties
顺便说一下,我数据库用的是mysql.8.0,如果版本不一样,需要修改依赖的版本
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/study?characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
log4j
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:/Mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
log4j.rootLogger=debug, stdout
springMybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.study.Service"></context:component-scan>
<!--加载properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源-->
<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- Session工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:sqlMapConfig.xml" />
<!--加载mapper.xml 文件-->
<property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"></property>
<!-- 配置别名 -->
<property name="typeAliasesPackage" value="com.study.Entity"></property>
</bean>
<!--接口扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.study.Mapper" />
</bean>
</beans>
sqlMapconfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 开启驼峰自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
</configuration>**
springMVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.study.Controller"></context:component-scan>
<!--自定义消息转换器的编码,解决后台传输json回前台时,中文乱码问题-->
<mvc:annotation-driven >
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter" >
<property name = "supportedMediaTypes">
<list>
<value>application/json;charset=utf-8</value>
<value>text/html;charset=utf-8</value>
<!-- application 可以在任意 form 表单里面 enctype 属性默认找到 -->
<value>application/x-www-form-urlencoded</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" ></bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/**"/>
<!-- 哪些方法不进行拦截-->
<mvc:exclude-mapping path="/user/login"/>
<mvc:exclude-mapping path="/user/register"/>
<!-- 注册拦截器对象 -->
<bean class="com.study.utils.TokenIeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!-- 接口跨域配置 -->
<mvc:cors>
<mvc:mapping path="/**"
allowed-origins="*"
allowed-methods="POST, GET, OPTIONS, DELETE, PUT"
allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
allow-credentials="true" />
</mvc:cors>
</beans>
实体类
在此我就不粘贴get/set方法了
result类,返回的结果集
public class Result {
private String msg; //返回的信息
private boolean status; //是否成功
private Map Data; //返回的数据
}
user类
public class User {
private Integer id;
private String username;
private String password;
private String realname;
}
controller层的
@RestController
@RequestMapping("user")
public class loginController {
@Autowired
private loginService loginService;
/**
* 注册账号
* @param user
* @return
*/
@RequestMapping("register")
public Result register(User user){
//将密码通过MD5进行加密,再存进去
user.setPassword(MD5Utils.MD5Encrypt(user.getPassword()));
//注册账号
int register = loginService.register(user);
if(register>0){
return new Result("注册成功",true, null);
}
return new Result("注册失败",false, null);
}
/**
* 登录并生成token
* @param user
* @return
*/
@RequestMapping("login")
public Result login(User user){
//将用户的密码进行加密
user.setPassword(MD5Utils.MD5Encrypt(user.getPassword()));
//调用登录方法
User loginUser = loginService.login(user);
//如果登录的用户存在的话,生成token
if(loginUser!=null){
//生成token
String token = JwtUtils.sign(loginUser.getUsername(), loginUser.getId());
if (token!=null){
return new Result("登录成功",true, null);
}else {
return new Result("token为空",false, null);
}
}
return new Result("该用户不存在",false, null);
}
}
service层代码
public interface loginService {
User login(User user);
int register(User user);
}
实现类的代码
在实现登录的时候,因为密码进行了加密,所以我在这里是先将登录的密码进行加密,然后到数据库去查询看是否有对应用户。
但这样并不安全,因为一般的MD5加密的话同样的密码加密结果是一样,如果多个用户密码一样的话,一旦破译会使多个用户受到牵连。
如何解决?
可以使用BCrypt+SpringSecurity进行解决,
大体思路是:
(1)在注册账号时候通过BCrypt(BCrypt可以理解成md5+随机salt)对密码进行加密,将加密的密码存入数据库
(2)登录的时候输入用户名和密码,根据用户名去数据库中查询用户信息,
(3)SpringSecurity框架会将输入的明文密码使用同样的算法(md5+同样的salt)进行加密和数据库中查询出来的密文密码进行比对
感兴趣的话大家可以先搜索一下,我之后会在总结如何使用的
@Service
public class loginServiceImpl implements loginService {
@Autowired
private loginMapper loginMapper;
@Override
public int register(User user) {
int register = loginMapper.register(user);
return register;
}
@Override
public User login(User user) {
//将用户的密码进行加密
User loginUser = loginMapper.login(user);
return loginUser;
}
}
Mapper层
@Repository
public interface loginMapper {
User login(User user);
int register(User user);
}
Mapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.Mapper.loginMapper">
<!-- 注册用户-->
<insert id="register" parameterType="User">
insert into User(username,password) values(#{username},#{password})
</insert>
<!-- 根据用户名查找用户-->
<select id="selectUser" parameterType="User" resultType="User">
select * from User where username=#{username}
</select>
<!-- 根据用户名和密码查找用户-->
<select id="login" parameterType="User" resultType="User">
select * from User where username=#{username} and password = #{password}
</select>
</mapper>
工具类:
JwtUtils .java:用于生成token和验证token的,这里面使用的是jwt生成的,借鉴于网上的资料,token会比较长,token的生成方式还有很多种,仅供参考
token生成详细解释: https://blog.csdn.net/KKKun_Joe/article/details/81878231
public class JwtUtils {
//过期时间20分钟
private static final long EXPIRE_TIME=20*60*1000;
private static final String TOKEN_SELECT="ZCEQIUBFKSJBFJH2020BQWE";
/**
* 加密
* @return
*/
/*生成token令牌*/
public static String sign(String username,int userId) {
try {
//过期时间
Date date=new Date(System.currentTimeMillis());
//设置加密算法和密匙
Algorithm algorithm = null;
algorithm = Algorithm.HMAC256(TOKEN_SELECT);
//设置头部信息
Map<String,Object> header=new HashMap<>(2);
header.put("typ","JWT");
header.put("alg","HS256");
//附带username,userID 的信息,生成签名
String sign = JWT.create().withHeader(header)
.withClaim("loginName", username)
.withClaim("user_id", userId)
.withExpiresAt(date)
.sign(algorithm);
return sign;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
/**
* 验证token
*/
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SELECT);
JWTVerifier verify = JWT.require(algorithm).build();
DecodedJWT verify1 = verify.verify(token);
return true;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return false;
}
}
}
TokenIeInterceptor .java
这是一个拦截器,在调用接口的时候必须通过拦截器,进行过滤,jdk1.8中HandlerInterceptor 都是默认方法,所以必须在配置文件中进行配置才可以重写生效,请查看springmvc.xml
public class TokenIeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("UTF-8");
String token =request.getHeader("token");
if (token!=null){
//验证token
boolean result = JwtUtils.verify(token);
if (result){
return true;
}
}
return false;
}
}
SimpleCORSFilter .java
因为token的生成是参考网上的,也许是我的问题,所以前端调用接口的时候显示跨域,这个类用于解决跨域问题,也需要在配置文件里配置,如果不存在跨域可无视
public class SimpleCORSFilter implements Filter {
private boolean isCross = false;
@Override
public void destroy() {
isCross = false;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isCross) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
System.out.println("拦截请求: " + httpServletRequest.getServletPath());
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpServletResponse.setHeader("Access-Control-Max-Age", "0");
httpServletResponse.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("XDomainRequestAllowed", "1");
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String isCrossStr = filterConfig.getInitParameter("IsCross");
isCross = isCrossStr.equals("true") ? true : false;
System.out.println(isCrossStr);
}
}
MD5Utils.java
public class MD5Utils {
public static String MD5Encrypt(String str){
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte[]byteDigest = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < byteDigest.length; offset++) {
i = byteDigest[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
return buf.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
有些东西也是参考网上的资料,会有不足,仅供参考