社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
首先创建RBAC 权限系统表
/*
MySQL Backup
Database: test
Backup Time: 2018-09-12 11:53:20
*/
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `test`.`role`;
DROP TABLE IF EXISTS `test`.`user`;
DROP TABLE IF EXISTS `test`.`user_role`;
CREATE TABLE `role` (
`role_id` int(11) NOT NULL,
`role_name` varchar(255) DEFAULT NULL,
`version` timestamp(6) NULL DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
`user_name` varchar(40) NOT NULL DEFAULT '未命名' COMMENT '用户名',
`user_pwd` varchar(255) NOT NULL DEFAULT '未命名' COMMENT '用户密码',
`version` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '版本号',
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
BEGIN;
LOCK TABLES `test`.`role` WRITE;
DELETE FROM `test`.`role`;
INSERT INTO `test`.`role` (`role_id`,`role_name`,`version`) VALUES (1, 'ROLE_ADMIN', '2018-08-14 12:15:49.000000'),(2, 'ROLE_USER', '2018-08-08 12:16:04.000000');
UNLOCK TABLES;
COMMIT;
BEGIN;
LOCK TABLES `test`.`user` WRITE;
DELETE FROM `test`.`user`;
INSERT INTO `test`.`user` (`user_id`,`user_name`,`user_pwd`,`version`) VALUES (1, 'admin', '$2a$10$.8baftxWLye9qoSsLZCR9OrkCyE/TmBmlc5hWd0xCCWiIb20CuLUe', '2018-08-24 18:59:58'),(2, 'login', '$2a$10$.8baftxWLye9qoSsLZCR9OrkCyE/TmBmlc5hWd0xCCWiIb20CuLUe', '2018-08-21 12:16:39'),(3, 'zhangsan', '$2a$10$mp0UA9FgWDahU0vMGojiAuS862.LG4FFNpAkBy3skEGCyYTeXcEx.', '2018-08-27 11:01:52'),(4, 'gaoadmin', '123', '2018-08-28 10:29:35'),(5, 'gaouser', '123', '2018-08-28 10:29:51');
UNLOCK TABLES;
COMMIT;
BEGIN;
LOCK TABLES `test`.`user_role` WRITE;
DELETE FROM `test`.`user_role`;
INSERT INTO `test`.`user_role` (`id`,`user_id`,`role_id`) VALUES (1, 1, 1),(2, 1, 1),(3, 2, 2),(4, 4, 1),(5, 5, 2);
UNLOCK TABLES;
COMMIT;
第一步:添加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.longfor</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>security</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.14.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<!--SpringSecurity支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
第二步:SpringBoot 配置 application.properties
#数据源配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#服务端口配置
server.port=8080
server.servlet.context-path=/security
#thymeleaf模板的配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
#AOP日志配置
spring.aop.auto=true
spring.aop.proxy-target-class=false
management.endpoint.health.show-details=always
#dao层日志执行打印
logging.level.com.longfor.security.dao=debug
#对SpringSecurity日志的打印
logging.level.org.springframework.security=info
#jwt
jwt.secret=mySecret
jwt.header=Authorization
jwt.expiration=604800
jwt.tokenHead=Bearer
#redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
# spring session使用存储类型
spring.session.store-type=redis
第三步 对User对象的封装
package com.longfor.security.bean;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import lombok.Data;
/**
* @Author: gaoleijie.
* @Description:
* @Date:Created in 2018/8/24 16:56.
*/
@Data
public class User implements UserDetails {
private Integer userId;
private String userName;
private String userPwd;
private Date version;
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getRolename()));
}
return authorities;
}
@Override
public String getPassword() {
return userPwd;
}
@Override
public String getUsername() {
return userName;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
第四部 对Userservice的封装
package com.longfor.security.service;
import com.longfor.security.bean.User;
import com.longfor.security.dao.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @Author: gaoleijie.
* @Description:
* @Date:Created in 2018/8/23 18:25.
*/
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user =userMapper.selectUserByUsername(s);
if(null == user){
throw new UsernameNotFoundException(String.format("未找到名字为'%s'.",s));
}
return user;
}
}
第五步:SecurityConfig
package com.longfor.security.config.security;
import com.longfor.security.config.jwt.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @Author: gaoleijie.
* @Description:SpringSecurity 核心配置类
* @Date:Created in 2018/8/25 22:01.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());//添加自定义的userDetailsService认证
}
// 装载BCrypt密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public GoAuthenticationSuccessHandler goAuthenticationSuccessHandler(){
return new GoAuthenticationSuccessHandler();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
/** http
// 关闭csrf
.csrf().disable()
// .anonymous().disable()
//.cors().and().httpBasic()
.authorizeRequests()
// 任何用户都可以访问URL以"/resources/", equals "/signup", 或者 "/about"开头的URL。
.antMatchers("/resources/**","/login").permitAll()
//以 "/admin/" 开头的URL只能由拥有 "ROLE_ADMIN"角色的用户访问。请注意我们使用 hasRole 方法
.antMatchers("/admin/**").hasRole("ADMIN")
// 任何以"/db/" 开头的URL需要用户同时具有 "ROLE_ADMIN" 和 "ROLE_DBA"。.
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
// 尚未匹配的任何URL要求用户进行身份验证
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login")
// 登陆成功
.loginProcessingUrl("/login").defaultSuccessUrl("/index",true)
/** // 认证成功
.successHandler(new GoAuthenticationSuccessHandler())
// 认证失败
.failureHandler(new GoAuthenticationFailureHandler())
.and().exceptionHandling()
// 已经认证的用户访问自己没有权限的资源处理
.accessDeniedHandler(new GoAccessDeniedHandler())
// 未经过认证的永固访问受保护的资源
.authenticationEntryPoint(new GoAuthenticationEntryPoint())**/
/**.and().logout().permitAll()
// 注销功能
//.logoutUrl("/login")
// 注销之后跳转的URL。默认是/login?logout。具体查看 the JavaDoc文档.
// .logoutSuccessUrl("/login")
// 让你设置定制的 LogoutSuccessHandler。如果指定了这个选项那么logoutSuccessUrl()的设置会被忽略
// .logoutSuccessHandler()
// 指定是否在注销时让HttpSession无效。 默认设置为 true。
.invalidateHttpSession(true)
// 允许指定在注销成功时将移除的cookie。
.deleteCookies(""JSESSIONID"")
// cookie 失效时间,默认有效期为14天
.and().rememberMe()
.tokenValiditySeconds(1800)
.key("token_key");*/
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT,关闭token
.and()
.httpBasic()
// 未经过认证的用户访问受保护的资源
.authenticationEntryPoint(new GoAuthenticationEntryPoint())
.and()
.authorizeRequests()
// 任何用户都可以访问URL以"/resources/", equals "/signup", 或者 "/about"开头的URL。
.antMatchers("/resources/**", "/login", "/auth/**").permitAll()
//以 "/admin/" 开头的URL只能由拥有 "ROLE_ADMIN"角色的用户访问。请注意我们使用 hasRole 方法
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/uuu").access("hasRole('USER') or hasRole('ADMIN') ")
.anyRequest().authenticated()
.and()
.formLogin()
// .loginPage("/login")
//.loginProcessingUrl("/login").defaultSuccessUrl("/index", true).failureUrl("/login?error")
.successHandler(goAuthenticationSuccessHandler())
// 认证失败
.failureHandler(new GoAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new GoLogoutSuccessHandler())
.permitAll();
// 记住我
http.rememberMe().rememberMeParameter("remember-me")
.userDetailsService(userDetailsService).tokenValiditySeconds(300);
http.exceptionHandling()
// 已经认证的用户访问自己没有权限的资源处理
.accessDeniedHandler(new GoAccessDeniedHandler())
.and().addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
SpringSecurity 登陆成功后或者登陆失败后,可进行配置到指定的类进行后续处理
有的用到了,有的没有用到,具体可根据自己的实际情况去处理
package com.longfor.security.config.security;
import com.alibaba.fastjson.JSON;
import com.longfor.security.bean.ResponseBean;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:如果用户已经通过身份验证,试图访问受保护的(该用户没有权限的)资源
* @Date:Created in 2018/8/25 23:06.
*/
public class GoAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
ResponseBean responseBean=new ResponseBean(403,"您的权限不足,无法访问该资源",null,null);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBean));
httpServletResponse.getWriter().flush();
}
}
package com.longfor.security.config.security;
import com.alibaba.fastjson.JSON;
import com.longfor.security.bean.ResponseBean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:它负责启动未经过身份验证的用户的身份验证过程(当他们试图访问受保护的资源
* @Date:Created in 2018/8/25 23:11.
*/
public class GoAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
// httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
// httpServletResponse.getWriter().print("{"code":401,"未登陆时无法访问该资源":""+e.getMessage()+""}");
// httpServletResponse.getWriter().flush();
//
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
ResponseBean responseBean=new ResponseBean(403,"无法访问该资源",null,null);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBean));
httpServletResponse.getWriter().flush();
}
}
package com.longfor.security.config.security;
import com.alibaba.fastjson.JSON;
import com.longfor.security.bean.ResponseBean;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:如果身份验证失败时调用
* @Date:Created in 2018/8/25 23:01.
*/
public class GoAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
// httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
// httpServletResponse.getWriter().print("{"code":1,"message":""+e.getMessage()+""}");
// httpServletResponse.getWriter().flush();
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
ResponseBean responseBean=new ResponseBean(401,"验证失败",null,null);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBean));
httpServletResponse.getWriter().flush();
}
}
package com.longfor.security.config.security;
import com.alibaba.fastjson.JSON;
import com.longfor.security.bean.ResponseBean;
import com.longfor.security.config.jwt.JwtTokenUtil;
import com.longfor.security.config.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:用于处理一个成功的身份验证实现执行是处理导航到后续的目标.
* @Date:Created in 2018/8/25 22:57.
*/
public class GoAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
JwtTokenUtil jwtTokenUtil;
/**
* //有效期
*/
@Value("${jwt.expiration}")
private Long expiration;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
// httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
// httpServletResponse.getWriter().print("{"code":0,"message":"登录成功"}");
// httpServletResponse.getWriter().flush();
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String jwtToken = jwtTokenUtil.generateToken(userDetails);
jwtTokenUtil.setExpire(jwtToken,userDetails.getUsername(),expiration+100000);
ResponseBean responseBean = new ResponseBean(200, "登录成功", jwtToken);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBean));
httpServletResponse.getWriter().flush();
}
}
package com.longfor.security.config.security;
import com.alibaba.fastjson.JSON;
import com.longfor.security.bean.ResponseBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:
* @Date:Created in 2018/9/4 14:07.
*/
public class GoLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
ResponseBean responseBean=new ResponseBean(200,"注销成功",null,null);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBean));
httpServletResponse.getWriter().flush();
}
}
到该步SpringSecurity基本已经配置完毕了
下面该继承JWT了
第一步 添加过滤器
package com.longfor.security.config.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: gaoleijie.
* @Description:
* @Date:Created in 2018/8/26 18:30.
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
private UserDetailsService userDetailsService;
private JwtTokenUtil jwtTokenUtil;
@Autowired
public JwtAuthenticationTokenFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {
this.userDetailsService = userDetailsService;
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader(tokenHeader);
if (authHeader != null && authHeader.startsWith(tokenHead)) {
String authToken = authHeader.substring(tokenHead.length());
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(jwtTokenUtil.validateToken(authToken)) {
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
jwtTokenUtil.del(authToken);
}
}
}
}
filterChain.doFilter(request, response);
}
}
工具类
package com.longfor.security.config.jwt;
import com.longfor.security.bean.User;
import com.longfor.security.config.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
* @Author: gaoleijie.
* @Description:JWT工具类
* @Date:Created in 2018/8/26 18:27.
*/
@Component
public class JwtTokenUtil implements Serializable {
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
private static final long serialVersionUID = -8305152446124853696L;
@Autowired
RedisUtil redisUtil;
/**
* 密钥
*/
@Value("${jwt.secret}")
private String secret;
/**
* //有效期
*/
@Value("${jwt.expiration}")
private Long expiration;
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 将token存储到redis
*/
public void setExpire(String key, String val, long time) {
redisUtil.setExpire(key, val, time);
}
/**
* 移除
*/
public void del(String key) {
redisUtil.del(key);
}
/**
* 判断是否有效
* @param authToken
* @return
*/
public Boolean validateToken(String authToken) {
Object o = redisUtil.get(authToken);
if(null != o){
return true;
}
return false;
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成令牌
*
* @param userDetails 用户
* @return 令牌
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(2);
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 判断令牌是否过期
*
* @param token 令牌
* @return 是否过期
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 验证令牌
*
* @param token 令牌
* @param userDetails 用户
* @return 是否有效
*/
public Boolean validateToken(String token, UserDetails userDetails) {
User user = (User) userDetails;
String username = getUsernameFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token));
}
}
下面是Redis工具类
package com.longfor.security.config.redis;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
/**
*
* @Description
*Redis工具类
* @author gaoleijie
* @Date 2018年1月15日
*/
@Component
@Slf4j
public class RedisUtil {
/**
* 默认编码
*/
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
/**
* Spring Redis Template
*/
private RedisTemplate<String, String> redisTemplate;
private JedisConnectionFactory jedisConnectionFactory;
public RedisUtil(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 添加到带有 过期时间的 缓存
*
* @param key redis主键
* @param value 值
* @param time 过期时间
*/
public void setExpire(final byte[] key, final byte[] value, final long time) {
redisTemplate.execute((RedisCallback<Long>) connection -> {
connection.set(key, value);
connection.expire(key, time);
log.info("[redisTemplate redis]放入 缓存 url:{} ========缓存时间为{}秒", key, time);
return 1L;
});
}
/**
* 添加到带有 过期时间的 缓存
*
* @param key redis主键
* @param value 值
* @param time 过期时间
*/
public void setExpire(final String key, final String value, final long time) {
redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
byte[] keys = serializer.serialize(key);
byte[] values = serializer.serialize(value);
connection.set(keys, values);
connection.expire(keys, time);
log.info("[redisTemplate redis]放入 缓存 url:{} ========缓存时间为{}秒", key, time);
return 1L;
});
}
/**
* 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销
*
* @param keys redis主键数组
* @param values 值数组
* @param time 过期时间
*/
public void setExpire(final String[] keys, final String[] values, final long time) {
redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
for (int i = 0; i < keys.length; i++) {
byte[] bKeys = serializer.serialize(keys[i]);
byte[] bValues = serializer.serialize(values[i]);
connection.set(bKeys, bValues);
connection.expire(bKeys, time);
log.info("[redisTemplate redis]放入 缓存 url:{} ========缓存时间为:{}秒", keys[i], time);
}
return 1L;
});
}
/**
* 一次性添加数组到 过期时间的 缓存,不用多次连接,节省开销
*
* @param keys the keys
* @param values the values
*/
public void set(final String[] keys, final String[] values) {
redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
for (int i = 0; i < keys.length; i++) {
byte[] bKeys = serializer.serialize(keys[i]);
byte[] bValues = serializer.serialize(values[i]);
connection.set(bKeys, bValues);
log.info("[redisTemplate redis]放入 缓存 url:{}", keys[i]);
}
return 1L;
});
}
/**
* 添加到缓存
*
* @param key the key
* @param value the value
*/
public void set(final String key, final String value) {
redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
byte[] keys = serializer.serialize(key);
byte[] values = serializer.serialize(value);
connection.set(keys, values);
log.info("[redisTemplate redis]放入 缓存 url:{}", key);
return 1L;
});
}
/**
* 查询在这个时间段内即将过期的key
*
* @param key the key
* @param time the time
* @return the list
*/
public List<String> willExpire(final String key, final long time) {
final List<String> keysList = new ArrayList<>();
redisTemplate.execute((RedisCallback<List<String>>) connection -> {
Set<String> keys = redisTemplate.keys(key + "*");
for (String key1 : keys) {
Long ttl = connection.ttl(key1.getBytes(DEFAULT_CHARSET));
if (0 <= ttl && ttl <= 2 * time) {
keysList.add(key1);
}
}
return keysList;
});
return keysList;
}
/**
* 查询在以keyPatten的所有 key
*
* @param keyPatten the key patten
* @return the set
*/
public Set<String> keys(final String keyPatten) {
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> redisTemplate.keys(keyPatten + "*"));
}
/**
* 根据key获取对象
*
* @param key the key
* @return the byte [ ]
*/
public byte[] get(final byte[] key) {
byte[] result = redisTemplate.execute((RedisCallback<byte[]>) connection -> connection.get(key));
log.info("[redisTemplate redis]取出 缓存 url:{} ", key);
return result;
}
/**
* 根据key获取对象
*
* @param key the key
* @return the string
*/
public String get(final String key) {
String resultStr = redisTemplate.execute((RedisCallback<String>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
byte[] keys = serializer.serialize(key);
byte[] values = connection.get(keys);
return serializer.deserialize(values);
});
log.info("[redisTemplate redis]取出 缓存 url:{} ", key);
return resultStr;
}
/**
* 根据key获取对象
*
* @param keyPatten the key patten
* @return the keys values
*/
public Map<String, String> getKeysValues(final String keyPatten) {
log.info("[redisTemplate redis] getValues() patten={} ", keyPatten);
return redisTemplate.execute((RedisCallback<Map<String, String>>) connection -> {
RedisSerializer<String> serializer = getRedisSerializer();
Map<String, String> maps = new HashMap<>();
Set<String> keys = redisTemplate.keys(keyPatten + "*");
for (String key : keys) {
byte[] bKeys = serializer.serialize(key);
byte[] bValues = connection.get(bKeys);
String value = serializer.deserialize(bValues);
maps.put(key, value);
}
return maps;
});
}
/**
* Ops for hash hash operations.
*
* @return the hash operations
*/
public HashOperations<String, String, String> opsForHash() {
return redisTemplate.opsForHash();
}
/**
* 对HashMap操作
*
* @param key the key
* @param hashKey the hash key
* @param hashValue the hash value
*/
public void putHashValue(String key, String hashKey, String hashValue) {
log.info("[redisTemplate redis] putHashValue() key={},hashKey={},hashValue={} ", key, hashKey, hashValue);
opsForHash().put(key, hashKey, hashValue);
}
/**
* 获取单个field对应的值
*
* @param key the key
* @param hashKey the hash key
* @return the hash values
*/
public Object getHashValues(String key, String hashKey) {
log.info("[redisTemplate redis] getHashValues() key={},hashKey={}", key, hashKey);
return opsForHash().get(key, hashKey);
}
/**
* 根据key值删除
*
* @param key the key
* @param hashKeys the hash keys
*/
public void delHashValues(String key, Object... hashKeys) {
log.info("[redisTemplate redis] delHashValues() key={}", key);
opsForHash().delete(key, hashKeys);
}
/**
* key只匹配map
*
* @param key the key
* @return the hash value
*/
public Map<String, String> getHashValue(String key) {
log.info("[redisTemplate redis] getHashValue() key={}", key);
return opsForHash().entries(key);
}
/**
* 批量添加
*
* @param key the key
* @param map the map
*/
public void putHashValues(String key, Map<String, String> map) {
opsForHash().putAll(key, map);
}
/**
* 集合数量
*
* @return the long
*/
public long dbSize() {
return redisTemplate.execute(RedisServerCommands::dbSize);
}
/**
* 清空redis存储的数据
*
* @return the string
*/
public String flushDB() {
return redisTemplate.execute((RedisCallback<String>) connection -> {
connection.flushDb();
return "ok";
});
}
/**
* 判断某个主键是否存在
*
* @param key the key
* @return the boolean
*/
public boolean exists(final String key) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.exists(key.getBytes(DEFAULT_CHARSET)));
}
/**
* 删除key
*
* @param keys the keys
* @return the long
*/
public long del(final String... keys) {
return redisTemplate.execute((RedisCallback<Long>) connection -> {
long result = 0;
for (String key : keys) {
result = connection.del(key.getBytes(DEFAULT_CHARSET));
}
return result;
});
}
/**
* 获取 RedisSerializer
*
* @return the redis serializer
*/
protected RedisSerializer<String> getRedisSerializer() {
return redisTemplate.getStringSerializer();
}
/**
* 对某个主键对应的值加一,value值必须是全数字的字符串
*
* @param key the key
* @return the long
*/
public long incr(final String key) {
return redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> redisSerializer = getRedisSerializer();
return connection.incr(redisSerializer.serialize(key));
});
}
/**
* redis List 引擎
*
* @return the list operations
*/
public ListOperations<String, String> opsForList() {
return redisTemplate.opsForList();
}
/**
* redis List数据结构 : 将一个或多个值 value 插入到列表 key 的表头
*
* @param key the key
* @param value the value
* @return the long
*/
public Long leftPush(String key, String value) {
return opsForList().leftPush(key, value);
}
/**
* redis List数据结构 : 移除并返回列表 key 的头元素
*
* @param key the key
* @return the string
*/
public String leftPop(String key) {
return opsForList().leftPop(key);
}
/**
* redis List数据结构 :将一个或多个值 value 插入到列表 key 的表尾(最右边)。
*
* @param key the key
* @param value the value
* @return the long
*/
public Long in(String key, String value) {
return opsForList().rightPush(key, value);
}
/**
* redis List数据结构 : 移除并返回列表 key 的末尾元素
*
* @param key the key
* @return the string
*/
public String rightPop(String key) {
return opsForList().rightPop(key);
}
/**
* redis List数据结构 : 返回列表 key 的长度 ; 如果 key 不存在,则 key 被解释为一个空列表,返回 0 ; 如果 key 不是列表类型,返回一个错误。
*
* @param key the key
* @return the long
*/
public Long length(String key) {
return opsForList().size(key);
}
/**
* redis List数据结构 : 根据参数 i 的值,移除列表中与参数 value 相等的元素
*
* @param key the key
* @param i the
* @param value the value
*/
public void remove(String key, long i, String value) {
opsForList().remove(key, i, value);
}
/**
* redis List数据结构 : 将列表 key 下标为 index 的元素的值设置为 value
*
* @param key the key
* @param index the index
* @param value the value
*/
public void set(String key, long index, String value) {
opsForList().set(key, index, value);
}
/**
* redis List数据结构 : 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 end 指定。
*
* @param key the key
* @param start the start
* @param end the end
* @return the list
*/
public List<String> getList(String key, int start, int end) {
return opsForList().range(key, start, end);
}
/**
* redis List数据结构 : 批量存储
*
* @param key the key
* @param list the list
* @return the long
*/
public Long leftPushAll(String key, List<String> list) {
return opsForList().leftPushAll(key, list);
}
/**
* redis List数据结构 : 将值 value 插入到列表 key 当中,位于值 index 之前或之后,默认之后。
*
* @param key the key
* @param index the index
* @param value the value
*/
public void insert(String key, long index, String value) {
opsForList().set(key, index, value);
}
/**
* 利用redis的单线程原子自增性保证数据自增的唯一性
*
* @param key
* @return
*/
public RedisAtomicLong getRedisAtomicLong(String key) {
return new RedisAtomicLong(key, jedisConnectionFactory);
}
/**
* ZINCRBY key increment member
*
* @param key
* @param increment
* @param member
*/
public void doZincrby(String key, Integer increment, String member) {
redisTemplate.execute((RedisCallback<Double>) connection -> {
RedisSerializer<String> redisSerializer = getRedisSerializer();
return connection.zIncrBy(redisSerializer.serialize(key), increment, redisSerializer.serialize(member));
});
}
/**
* ZREVRANGE key start stop [WITHSCORES]
*
* @return
*/
public List<String> doZrevrange(String key, Integer start, Integer end) {
List<String> stringList = new ArrayList<>();
RedisSerializer<String> redisSerializer = getRedisSerializer();
Set<byte[]> strBytes = redisTemplate.execute((RedisCallback<Set<byte[]>>) connection -> connection.zRevRange(redisSerializer.serialize(key), start, end));
Iterator byteIter = strBytes.iterator();
while (byteIter.hasNext()) {
stringList.add(redisSerializer.deserialize((byte[]) byteIter.next()));
}
return stringList;
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!