作者 朱兆平

集成JWT框架,做到完全的前后端分离

... ... @@ -172,7 +172,7 @@
<!--开发环境:打印控制台-->
<springProfile name="dev">
<logger name="org.springframework.security" level="info"/>
<logger name="org.springframework.security" level="trace"/>
<logger name="org.apache.tomcat" level="info" />
<logger name="com.tianbo.warehouse.dao" level="DEBUG" />
<root level="INFO">
... ...
... ... @@ -60,6 +60,15 @@
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
... ...
... ... @@ -22,6 +22,9 @@
* url角色权限识别
* menu与权限关联
* 参数校验
* 集成JWT JAVA Web Token框架
* 前后端完全分离
* 前端登录验证后,每次访问系统通过在头部携带带有JWT token的Authorization:Bearer "Tokens字符窜"访问系统
* 已集成mybatis、mybatisGenerator、pageHelper
* 集成定时任务框架
* 目前在IMF框架中使用,打开IMF_Task里面的定时任务注释就可以启动IMF客户端功能
... ...
... ... @@ -51,9 +51,9 @@ public class RequestRequireAOP {
for(int i =0;i<args.length; i++){
//class相等表示是同一个对象
if (args[i].getClass().getName().equals("java.lang.String")) {
if (null!=args[i] && args[i].getClass().getName().equals("java.lang.String")) {
if (null==args[i] || ((String)args[i]).isEmpty()){
if (((String)args[i]).isEmpty()){
args[i] = null;
}
}
... ...
package com.tianbo.warehouse.controller.response;
import lombok.Data;
import java.io.Serializable;
@Data
public class ResultJson implements Serializable{
private static final long serialVersionUID = 1L;
... ... @@ -11,6 +14,8 @@ public class ResultJson implements Serializable{
private String msg = "";
// 返回对象
private Object data = "";
//返回的JWT
private String jwtToken;
public ResultJson() {
}
... ... @@ -33,28 +38,4 @@ public class ResultJson implements Serializable{
this.msg = msg;
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
... ...
... ... @@ -31,7 +31,6 @@ public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor imp
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
... ...
... ... @@ -2,6 +2,7 @@ package com.tianbo.warehouse.security.config;
import com.netflix.discovery.converters.Auto;
import com.tianbo.warehouse.security.CustomUserDetailService;
import com.tianbo.warehouse.security.filter.JwtAuthenticationTokenFilter;
import com.tianbo.warehouse.security.handel.*;
import com.tianbo.warehouse.security.MyFilterSecurityInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -14,9 +15,11 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;
@Configuration
... ... @@ -50,6 +53,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//user Details Service验证
... ... @@ -109,7 +115,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.and()
.csrf().disable();
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
/**
* 配合JWT做的配置,前后端完全分离,前端与后端不在一台服务器上的配置
*/
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 禁用headers缓存
http.headers().cacheControl();
//关闭session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//session管理
//session失效后跳转
... ...
package com.tianbo.warehouse.security.filter;
import com.tianbo.warehouse.security.CustomUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 访问时判定JAVA WEB TOKEN,是否有TOKEN,有TOKEN是否超时,
* 正常则取出TOKEN ,从TOKEN中获取用户名,赋予系统登录。
* 注意此过滤器每次都会被访问,每个URL带TOKEN 访问这里然后去查用户的资料 会造成数据库压力。
* !!!!后期要把用户资料存储在Redis中,然后用户资料从redis中取,减少数据库压力。
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter{
@Autowired
CustomUserDetailService userDetailService;
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException{
//请求头为 Authorization
//请求体为 Bearer token
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
final String authToken = authHeader.substring("Bearer ".length());
String username = JwtTokenUtil.parseToken(authToken);
//有JWT 没有登录,去JWT的 信息 获取用户信息,赋予登录
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailService.loadUserByUsername(username);
if (userDetails != null) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}
... ...
package com.tianbo.warehouse.security.filter;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
public class JwtTokenUtil {
//加载jwt.jks文件
private static InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jwt.jks");
private static PrivateKey privateKey = null;
private static PublicKey publicKey = null;
static {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(inputStream, "vmvnv1v2".toCharArray());
privateKey = (PrivateKey) keyStore.getKey("jwt", "vmvnv1v2".toCharArray());
publicKey = keyStore.getCertificate("jwt").getPublicKey();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String generateToken(String subject, int expirationSeconds) {
return Jwts.builder()
.setClaims(null)
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + expirationSeconds * 1000))
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
}
public static String parseToken(String token) {
String subject = null;
try {
Claims claims = Jwts.parser()
.setSigningKey(publicKey)
.parseClaimsJws(token).getBody();
subject = claims.getSubject();
} catch (Exception e) {
}
return subject;
}
}
... ...
... ... @@ -5,6 +5,7 @@ import com.tianbo.warehouse.bean.AuthSuccessResponse;
import com.tianbo.warehouse.controller.PermssionController;
import com.tianbo.warehouse.model.PERMISSION;
import com.tianbo.warehouse.model.USERS;
import com.tianbo.warehouse.security.filter.JwtTokenUtil;
import com.tianbo.warehouse.security.model.LoginType;
import com.tianbo.warehouse.service.PermissionService;
import org.apache.commons.logging.Log;
... ... @@ -50,9 +51,16 @@ public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticat
//将 authention 信息打包成json格式返回
response.setContentType("application/json;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin","*");
USERS loginedUser = (USERS) authentication.getPrincipal();
//返回前端的数据安全起见把password去掉
loginedUser.setPassword(null);
//设置用户的TOKEN的有效时间,下面是300秒=5分钟
String jwtToken = JwtTokenUtil.generateToken(loginedUser.getUsername(), 300);
response.setHeader("Authorization",jwtToken);
Map<String,Object> menuMap = permissionService.getUserMenus(loginedUser.getUserId());
response.getWriter().write(objectMapper.writeValueAsString(new AuthSuccessResponse(authentication,menuMap)));
}else {
... ...
不能预览此文件类型