SpringBoot 结合SpringSecurity+Jwt实现权限认证

首先创建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依赖



    4.0.0

    com.longfor
    security
    0.0.1-SNAPSHOT
    jar

    security
    Demo project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.4.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.springframework.security.oauth
            spring-security-oauth2
            2.0.14.RELEASE
        
        
            io.jsonwebtoken
            jjwt
            0.9.0
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            1.3.2
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
        org.thymeleaf.extras
        thymeleaf-extras-springsecurity4
        3.0.2.RELEASE
    
        
            com.alibaba
            fastjson
            1.2.47
        
        
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            io.jsonwebtoken
            jjwt
            0.9.0
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-aop
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
                org.mybatis.generator
                mybatis-generator-maven-plugin
                1.3.2
                
                    true
                    true
                
            

        
        
            
                src/main/java
                
                    **/*.xml
                
            
            
                src/main/resources
            
        
    



第二步: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 roles;


    @Override
    public Collection getAuthorities() {
        List 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 登陆成功后或者登陆失败后,可进行配置到指定的类进行后续处理

SpringBoot 结合SpringSecurity+Jwt实现权限认证_第1张图片

有的用到了,有的没有用到,具体可根据自己的实际情况去处理

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 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 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 redisTemplate;

    private JedisConnectionFactory jedisConnectionFactory;

    public RedisUtil(RedisTemplate 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) 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) connection -> {
            RedisSerializer 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) connection -> {
            RedisSerializer 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) connection -> {
            RedisSerializer 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) connection -> {
            RedisSerializer 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 willExpire(final String key, final long time) {
        final List keysList = new ArrayList<>();
        redisTemplate.execute((RedisCallback>) connection -> {
            Set 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 keys(final String keyPatten) {
        return redisTemplate.execute((RedisCallback>) connection -> redisTemplate.keys(keyPatten + "*"));
    }

    /**
     * 根据key获取对象
     *
     * @param key the key
     * @return the byte [ ]
     */
    public byte[] get(final byte[] key) {
        byte[] result = redisTemplate.execute((RedisCallback) 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) connection -> {
            RedisSerializer 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 getKeysValues(final String keyPatten) {
        log.info("[redisTemplate redis]  getValues()  patten={} ", keyPatten);
        return redisTemplate.execute((RedisCallback>) connection -> {
            RedisSerializer serializer = getRedisSerializer();
            Map maps = new HashMap<>();
            Set 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 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 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 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) connection -> {
            connection.flushDb();
            return "ok";
        });
    }

    /**
     * 判断某个主键是否存在
     *
     * @param key the key
     * @return the boolean
     */
    public boolean exists(final String key) {
        return redisTemplate.execute((RedisCallback) 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) connection -> {
            long result = 0;
            for (String key : keys) {
                result = connection.del(key.getBytes(DEFAULT_CHARSET));
            }
            return result;
        });
    }

    /**
     * 获取 RedisSerializer
     *
     * @return the redis serializer
     */
    protected RedisSerializer getRedisSerializer() {
        return redisTemplate.getStringSerializer();
    }

    /**
     * 对某个主键对应的值加一,value值必须是全数字的字符串
     *
     * @param key the key
     * @return the long
     */
    public long incr(final String key) {
        return redisTemplate.execute((RedisCallback) connection -> {
            RedisSerializer redisSerializer = getRedisSerializer();
            return connection.incr(redisSerializer.serialize(key));
        });
    }

    /**
     * redis List 引擎
     *
     * @return the list operations
     */
    public ListOperations 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 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 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) connection -> {
            RedisSerializer redisSerializer = getRedisSerializer();
            return connection.zIncrBy(redisSerializer.serialize(key), increment, redisSerializer.serialize(member));
        });
    }

    /**
     * ZREVRANGE key start stop [WITHSCORES]
     *
     * @return
     */
    public List doZrevrange(String key, Integer start, Integer end) {

        List stringList = new ArrayList<>();
        RedisSerializer redisSerializer = getRedisSerializer();
        Set strBytes = redisTemplate.execute((RedisCallback>) connection -> connection.zRevRange(redisSerializer.serialize(key), start, end));
        Iterator byteIter = strBytes.iterator();
        while (byteIter.hasNext()) {
            stringList.add(redisSerializer.deserialize((byte[]) byteIter.next()));
        }
        return stringList;
    }

}

具体源码:https://download.csdn.net/download/gaoleijie/10661346

你可能感兴趣的:(Java/Web开发)