demo(一)Spring Security简单后台实例

一、功能介绍:下面是一个springBoot+springSecurity+jwt 带验证码、单一点登录、token鉴权的简单后台

1、首先是获取验证码:

2、登录:输入用户名、密码、验证码(输入1111可以跳过验证码校验):

3、获取登录用户的权限:

二、代码:

1、pom:

<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.security</groupId>
  <artifactId>security-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.1.RELEASE</version>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.21</version>
    </dependency>
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>

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

    <!-- redis -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
    </dependency>
    <!--httpclient -->
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.6</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.31</version>
    </dependency>

    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
    </dependency>
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.5</version>
    </dependency>

  </dependencies>
</project>

2、application.properties:

server.port=9999
server.context-path=/test
#mysql
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:Mapper/*Mapper.xml

spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=
spring.redis.database=1
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=0

3、常量:

1)CommonEnums:
package com.demo.constants;

public enum CommonEnums {

    CODE_200(200,"请求成功"),
    CODE_300(300,"登录成功"),
    CODE_301(301,"登录失败"),
    CODE_302(302,"退出成功"),
    CODE_303(303,"退出失败"),
    CODE_304(304,"用户或密码错误"),
    CODE_305(305,"账户已被锁定"),
    CODE_401(401,"授权失败"),
    CODE_400(400,"业务异常"),
    CODE_402(402,"非法请求"),
    CODE_403(403,"验证码错误"),
    CODE_405(405,"验证码无效"),
    CODE_500(500,"系统异常"),

    ;
    public final int code;

    public final String message;

    CommonEnums(int code, String message ) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return this.code;
    }

    public String getMessage() {
        return message;
    }

    public static String getEnumToValue(int code){
        for (CommonEnums statusEnum: CommonEnums.values()) {
            if (statusEnum.code == code){
                return statusEnum.message;
            }
        }
        return "";
    }
}

2)、Constants:

package com.demo.constants;
public class Constants {

    public static final long EXPIRATIONTIME = (long)1000 * 60 * 60 * 24 * 1; //1 days
    public static final String SECRET = "spring-security-jwt";
    public static final String HEADER_STRING = "token";
    public static final String TOKEN_PREFIX = "Bearer";
    public static final String TOKEN_JWT="token_jwt:";
    public static final String JWT_USER="jwt_user:";
    public static final String LOCK="lock:";
    public static final String ERROR_NUM="error_num:";
}
4、dto:省略所有get、set方法

1)UserDTO用户dto:

public class UserDTO implements Serializable {
    private Integer id;

    private String userNickName;

    private String userName;

    private String password;
}

2)RoleVO角色:

public class RoleVO implements Serializable {
    private Integer roleId;

    private String roleCode;

    private String roleName;
}

3)AuthorityDTO权限

public class AuthorityDTO implements Serializable {

    private Integer authorityId;

    private String authorityCode;

    private String authorityName;
}

4)JwtAuthenticationRequest,用户登录vo,对前端请求对象的封装:

public class JwtAuthenticationRequest implements Serializable {

    private static final long serialVersionUID = -8445943548965154778L;

    private String username;
    private String password;
    private String code;

    public JwtAuthenticationRequest() {
        super();
    }

    public JwtAuthenticationRequest(String username, String password) {
        this.setUsername(username);
        this.setPassword(password);
    }

}

5)JwtUser系统登录用户:

public class JwtUser implements UserDetails {

    private Integer id;
    private String userName;
    private String password;
    private String userNickName;
    private List<RoleVO> roles = new ArrayList<>();
    private Collection<? extends GrantedAuthority> authorities;

    public JwtUser(){

    }

    public JwtUser(Integer id, String userName, String password, String userNickName,
                   Collection<? extends GrantedAuthority> authorities, List<RoleVO> roles){
        this.id = id;
        this.userName = userName;
        this.userNickName = userNickName;
        this.password = password;
        this.authorities = authorities;
        this.roles = roles;
    }
}

6)返回结果封装ResponseMessage:

package com.demo.dto;

import com.demo.constants.CommonEnums;

public class ResponseMessage {

    private int code;

    private String status;

    private String message;

    private String errorMessage;

    private String errorExceptionMessage;

    private Object data;

    public ResponseMessage(int code,String status,Object data){
        this.code = code;
        this.status = status;
        this.data = data;
    }

    public ResponseMessage(int code,String status,String message){
        this.code = code;
        this.status = status;
        this.message = message;
    }

    public ResponseMessage(int code,String status,String message,Object data){
        this.code = code;
        this.status = status;
        this.message = message;
        this.data = data;
    }




    public ResponseMessage(int code,String status,String message,String errorMessage){
        this.code = code;
        this.status = status;
        this.message = message;
        this.errorMessage = errorMessage;
    }

    public ResponseMessage(int code,String status,String message,String errorMessage,String errorExceptionMessage){
        this.code = code;
        this.status = status;
        this.message = message;
        this.errorMessage = errorMessage;
        this.errorExceptionMessage = errorExceptionMessage;
    }


    public static ResponseMessage  success(String message){
        return new ResponseMessage(CommonEnums.CODE_200.code,"success",message);
    }
    public static ResponseMessage  success(Object data){
        return new ResponseMessage(CommonEnums.CODE_200.code,"success",data);
    }


    public static ResponseMessage  success(String message,Object data){
        return new ResponseMessage(CommonEnums.CODE_200.code,"success",message,data);
    }

    public static ResponseMessage  success(int code,String message,Object data){
        return new ResponseMessage(code,"success",message,data);
    }

    public static ResponseMessage  success(){
        return new ResponseMessage(CommonEnums.CODE_200.code,null,CommonEnums.CODE_200.message);
    }

    public static  ResponseMessage success(int code,Object data){
        return new ResponseMessage(code,"success",data);
    }


    public static ResponseMessage success(int code,String message){
        return new ResponseMessage(code,"success",message);
    }

    public static  ResponseMessage error(int code,String message){
        return new ResponseMessage(code,"error",message);
    }

    public static  ResponseMessage error(int code,String message,String errorMessage){
        return new ResponseMessage(code,"error",message,errorMessage);
    }

    public static  ResponseMessage error(int code,String message,String errorMessage,String errorExceptionMessage){
        return new ResponseMessage(code,"error",message,errorMessage,errorExceptionMessage);
    }


    public static  ResponseMessage error(String message,String errorMessage){
        return new ResponseMessage(CommonEnums.CODE_500.code,"error",message,errorMessage);
    }

    public static  ResponseMessage error(String message){
        return new ResponseMessage(CommonEnums.CODE_400.code,"error",message);
    }

    public static  ResponseMessage infor(int code,String message){
        return new ResponseMessage(code,"infor",message);
    }

   }
 
}

4、factory:这块用于注册与获取spring的bean,做个统一的管理:

1)ApplicationContextRegister:

package com.demo.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


@Component
public class ApplicationContextRegister implements ApplicationContextAware {
    private static Logger logger = LoggerFactory.getLogger(ApplicationContextRegister.class);
    private static ApplicationContext APPLICATION_CONTEXT;
    /**
     * 设置spring上下文
     * @param applicationContext spring上下文
     * @throws BeansException
     * */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        logger.debug("ApplicationContext registed-->{}", applicationContext);
        DefaultBeanFactory.setSpringApplicationContext(applicationContext);
        APPLICATION_CONTEXT = applicationContext;
    }

    /**
     * 获取容器
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return APPLICATION_CONTEXT;
    }

    /**
     * 获取容器对象
     * @param type
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> type) {
        return APPLICATION_CONTEXT.getBean(type);
    }
}

2)DefaultBeanFactory:

package com.demo.factory;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class DefaultBeanFactory {
	private static ApplicationContext context = null;
	private static DefaultBeanFactory instance = null;
	private static Object lock = new Object();

	private DefaultBeanFactory(String filepath){
		try {
			context = new ClassPathXmlApplicationContext(filepath);
		} catch (Exception e) {
		}
	}
	@SuppressWarnings("static-access")
	private DefaultBeanFactory(ApplicationContext context){
		try {
			this.context = context;
		} catch (Exception e) {
		}
	}

	public static void setSpringApplicationContext(ApplicationContext context){
		synchronized (lock) {
			instance = new DefaultBeanFactory(context);
		}
	}
	public static DefaultBeanFactory getInstance() {
		if(instance == null || context == null){
			throw new RuntimeException("Spring context is null!");
		}
		return instance;
	}

	public static DefaultBeanFactory getInstance(String filepath) {
		synchronized (lock) {
			instance = new DefaultBeanFactory(filepath);
		}
		return instance;
	}
	public Object getBean(String name) {
		return context.getBean(name);
	}
}

3)ServiceFactory:

package com.demo.factory;

import com.demo.util.RedisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ServiceFactory {


    private static Logger logger = LoggerFactory.getLogger(ServiceFactory.class);

    private static ServiceFactory instance = new ServiceFactory();

    private final String REDIS_CLIENT_BEAN="redisClient";

    private final String USER_ACTION_LOG_SERVICE = "userActionLogService";


    public ServiceFactory() {
        // TODO Auto-generated constructor stub
    }

    public static ServiceFactory getInstance() {
        if (instance == null) {
            instance = new ServiceFactory();
        }
        return instance;
    }

    public RedisClient createRedisClient() {
        try {
            return (RedisClient) DefaultBeanFactory.getInstance().getBean(REDIS_CLIENT_BEAN);
        } catch (Exception e) {
            throw new RuntimeException("创建 createRedisClient BEAN 异常", e);
        }
    }


}

5、handle异常:

1)CustomAuthenticationFailureHandler:

package com.demo.handle;

import com.alibaba.fastjson.JSON;
import com.demo.constants.CommonEnums;
import com.demo.dto.ResponseMessage;
import org.apache.http.entity.ContentType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

  @Override
  public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                                      HttpServletResponse httpServletResponse, AuthenticationException e)
      throws IOException, ServletException {
    httpServletResponse.setContentType(ContentType.APPLICATION_JSON.toString());
    httpServletResponse.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_304.code,CommonEnums.CODE_304.message)));
  }
}

2)Http401AuthenticationEntryPoint:

package com.demo.handle;

import com.alibaba.fastjson.JSON;
import com.demo.constants.CommonEnums;
import com.demo.dto.ResponseMessage;
import org.apache.http.entity.ContentType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class Http401AuthenticationEntryPoint implements AuthenticationEntryPoint {


    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType(ContentType.APPLICATION_JSON.toString());
        response.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_401.code,CommonEnums.CODE_401.message)));
    }

}

6、服务:

1)自定义身份认证验证CustomAuthenticationProvider:

package com.demo.service.impl;


import com.demo.dto.UserDTO;
import com.demo.service.UserService;
import com.demo.util.AESUtils;
import com.demo.util.MD5Util;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.security.NoSuchAlgorithmException;
import java.util.Objects;

/**
 * 自定义身份认证验证组件
 */
public class CustomAuthenticationProvider implements AuthenticationProvider {
    private UserService userService;

    public CustomAuthenticationProvider(
                                        UserService userService
                                        ){
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取认证的用户名 & 密码
        //前端带入的username
        String username = authentication.getName();
        //前端带入的加密password
        String passwordRsa = authentication.getCredentials().toString();
        //passwordRsa解密后的密码
        String password = "";
        String passwordMd5 = "";
        //RSA解密
        try {
            password = AESUtils.decryptStr(passwordRsa,AESUtils.key);
        } catch (Exception e) {
            throw new UsernameNotFoundException("用户不存在");
        }

        UserDTO userDO = userService.findByUserName(username);
        if (userDO == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        //判断密码
        try {
            //md5加密
            passwordMd5 = MD5Util.MD5(password);
        } catch (NoSuchAlgorithmException e) {
            throw new UsernameNotFoundException("用户不存在");
        }
        if (!Objects.equals(passwordMd5,userDO.getPassword())){
            throw new BadCredentialsException("密码错误");
        }

        UserDetails userDetails = userService.loadUserByUsername(username);
        // 这里设置权限和角色
        Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
        return auth;
    }

    /**
     * 是否可以提供输入类型的认证服务
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

2)用户服务UserServiceImpl:

package com.demo.service.impl;

import com.demo.dao.AuthorityDao;
import com.demo.dao.UserDao;
import com.demo.dao.UserRoleDao;
import com.demo.dto.AuthorityDTO;
import com.demo.dto.JwtUser;
import com.demo.dto.RoleVO;
import com.demo.dto.UserDTO;
import com.demo.service.RoleService;
import com.demo.service.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserRoleDao userRoleDao;

    @Autowired
    private AuthorityDao authorityDao;

    @Autowired
    private RoleService roleService;

    @Override
    public UserDTO findByUserName(String userAccount) {
       return userDao.findByUserName(userAccount);
    }

    @Override
    public List<UserDTO> getAllUsers() {
        return userDao.getAllUsers();
    }

    @Override
    public List<AuthorityDTO> getAuthortiesByUserId(Integer userId) {
        List<RoleVO> roles = userRoleDao.findRoleByUserId(userId);
        if(CollectionUtils.isNotEmpty(roles)){
            List<Integer> roleIds = roles.stream().map(RoleVO::getRoleId).collect(Collectors.toList());
            return authorityDao.getAuthortiesByRoleIds(roleIds);
        }
        return new ArrayList<>();
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDTO userDO = findByUserName(username);
        if(userDO == null){
            throw new UsernameNotFoundException("用户不存在");
        }
        List<RoleVO> roleDOS = roleService.findRoleByUserId(userDO.getId());
        return new JwtUser(userDO.getId(),userDO.getUserName(),userDO.getPassword(),
                userDO.getUserNickName(),null,roleDOS);
    }
}

3)系统角色服务RoleServiceImpl:

package com.demo.service.impl;

import com.demo.dao.UserRoleDao;
import com.demo.dto.RoleVO;
import com.demo.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("roleService")
public class RoleServiceImpl implements RoleService {

    @Autowired
    private UserRoleDao userRoleDao;

    @Override
    public List<RoleVO> findRoleByUserId(Integer id) {
        return userRoleDao.findRoleByUserId(id);
    }
}

7、util:

1)AESUtils:

package com.demo.util;


import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class AESUtils {

    private static String iv = "HGty&6%4ojyUyhgy";//偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
    private static String Algorithm = "AES";
    private static String AlgorithmProvider = "AES/CBC/PKCS5Padding"; //算法/模式/补码方式
    public final static String key="FUjs@17654HGJKKn";

    public static byte[] generatorKey() throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
        keyGenerator.init(256);//默认128,获得无政策权限后可为192或256
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey.getEncoded();
    }

    public static IvParameterSpec getIv() throws UnsupportedEncodingException {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes("utf-8"));
        System.out.println("偏移量:"+byteToHexString(ivParameterSpec.getIV()));
        return ivParameterSpec;
    }

    public static byte[] encrypt(String src, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
        SecretKey secretKey = new SecretKeySpec(key, Algorithm);
        IvParameterSpec ivParameterSpec = getIv();
        Cipher cipher = Cipher.getInstance(AlgorithmProvider);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
        return cipherBytes;
    }

    public static byte[] decrypt(String src, byte[] key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, Algorithm);

        IvParameterSpec ivParameterSpec = getIv();
        Cipher cipher = Cipher.getInstance(AlgorithmProvider);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        byte[] hexBytes = hexStringToBytes(src);
        byte[] plainBytes = cipher.doFinal(hexBytes);
        return plainBytes;
    }

    /**
     * 解密
     * @param src
     * @param keyStr
     * @return
     * @throws Exception
     */
    public static String decryptStr(String src, String keyStr) throws Exception {

        byte key[] = keyStr.getBytes("utf-8");
        SecretKey secretKey = new SecretKeySpec(key, Algorithm);

        IvParameterSpec ivParameterSpec = getIv();
        Cipher cipher = Cipher.getInstance(AlgorithmProvider);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        byte[] hexBytes = hexStringToBytes(src);
        byte[] plainBytes = cipher.doFinal(hexBytes);
        return new String(plainBytes,"UTF-8");
    }


    public static String encrypt(String src, String keyStr) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {

        byte key[] = keyStr.getBytes("utf-8");
        SecretKey secretKey = new SecretKeySpec(key, Algorithm);
        IvParameterSpec ivParameterSpec = getIv();
        Cipher cipher = Cipher.getInstance(AlgorithmProvider);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
        return new String(cipherBytes,"UTF-8");
    }

    public static void main(String args[]){
        try {
            String passwordMd5 = MD5Util.MD5("123456");
            System.out.println("加密后密码:"+passwordMd5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 将byte转换为16进制字符串
     * @param src
     * @return
     */
    public static String byteToHexString(byte[] src) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xff;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                sb.append("0");
            }
            sb.append(hv);
        }
        return sb.toString();
    }

    /**
     * 将16进制字符串装换为byte数组
     * @param hexString
     * @return
     */
    public static byte[] hexStringToBytes(String hexString) {
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] b = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            b[i] = (byte) (charToByte(hexChars[pos]) << 4 | (charToByte(hexChars[pos + 1]))& 0xff);
        }
        return b;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }


     /*public static void main(String[] args) {
        try {
            // byte key[] = generatorKey();
            System.out.println("FUjs@17654HGJKKn".length());
            // 密钥必须是16的倍数
            byte key[] = "FUjs@17654HGJKKn".getBytes("utf-8");//hexStringToBytes("0123456789ABCDEF");
            String src = "usersrs=111111?sdfjsalkj=1mlkjklasjdfkls?sss=sdfsjlk1123123123?sdd=453456465432165765432221351567897654132";
            System.out.println("密钥:"+byteToHexString(key));
            System.out.println("原字符串:"+src);

            String enc = byteToHexString(encrypt(src, key));
            System.out.println("加密:"+enc);
            System.out.println("解密:"+decryptStr(enc, AESUtils.key));
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/


}

2)MD5Util

package com.demo.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Md5算法加密
 *
 * @author <a href="mailTo:helen@ibw.cn">Helen</a>
 * @time Mar 19, 2013
 * @version 1.0
 */
public class MD5Util {

    private final static Logger logger = LoggerFactory.getLogger(MD5Util.class);

    private final static String APP_KEY="hYHN#1son@16faEV2";
    private final static String CHARSET="UTF-8";
    /**
     * MD5加密算法
     *
     * @param s
     * @return
     */
    public final static String MD5(String s) throws NoSuchAlgorithmException {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'A', 'B', 'C', 'D', 'E', 'F' };
        byte[] btInput = s.getBytes();
        // 获得MD5摘要算法的 MessageDigest 对象
        MessageDigest mdInst;
        mdInst = MessageDigest.getInstance("MD5");
        // 使用指定的字节更新摘要
        mdInst.update(btInput);
        // 获得密文
        byte[] md = mdInst.digest();
        // 把密文转换成十六进制的字符串形式
        int j = md.length;
        char str[] = new char[j * 2];
        int k = 0;
        for (int i = 0; i < j; i++) {
            byte byte0 = md[i];
            str[k++] = hexDigits[byte0 >>> 4 & 0xf];
            str[k++] = hexDigits[byte0 & 0xf];
        }
        return new String(str);
    }


    /**
     * 编码转换
     * @param content
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    public static String sign(String prestr){
        String mysign = DigestUtils.md5Hex(getContentBytes(prestr + APP_KEY, CHARSET));
        return mysign;
    }

    public static String signParams(Map<String,String> params){
        try {
            if (params != null && params.size()>0){
                List<Map.Entry<String, String>> list = new ArrayList(params.entrySet());
                Collections.sort(list, new Comparator<Map.Entry<String,String>>() {
                    @Override
                    public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                        return o1.getKey().compareTo(o2.getKey());
                    }
                });
                StringBuffer sb = new StringBuffer();
                for (Map.Entry<String, String> ent:list) {
                    sb.append(ent.getKey());
                    sb.append(ent.getValue());
                }
                logger.info("原字符串:{}",sb.toString());
                return sign(sb.toString());
            }
        }catch (Exception e){

        }
        return "";
    }

    public static boolean isSign(HashMap<String,String> params,String sign){
        try{
            String newSign = signParams(params);
            logger.info("原签名:{}",sign);
            logger.info("新签名:{}",newSign);
            if (Objects.equals(sign,newSign)){
                return true;
            }
        }catch (Exception e){

        }
        return false;
    }



}

3)RedisClient:

package com.demo.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.*;

@Component
public class RedisClient {

    private  final Logger logger = LoggerFactory.getLogger(RedisClient.class);

    private  final String SUCCESS_OK = "OK";
    private  final Long SUCCESS_STATUS_LONG = 1L;

    // SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作
    private  final String SET_IF_NOT_EXIST = "NX";
    // 给key加一个过期的设置,具体时间由第五个参数决定
    private  final String SET_WITH_EXPIRE_TIME = "PX";

    private static JedisPool jedisPool;

    private static final String IP = "127.0.0.1"; // ip
    private static final int PORT = 6379; // 端口
    // private static final String AUTH=""; // 密码(原始默认是没有密码)
    private static int MAX_ACTIVE = 1024; // 最大连接数
    private static int MAX_IDLE = 200; // 设置最大空闲数
    private static int MAX_WAIT = 10000; // 最大连接时间
    private static int TIMEOUT = 10000; // 超时时间
    private static boolean BORROW = true; // 在borrow一个事例时是否提前进行validate操作

     /**
     * 初始化线程池
     */
    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(MAX_ACTIVE);
        config.setMaxIdle(MAX_IDLE);
        config.setMaxWaitMillis(MAX_WAIT);
        config.setTestOnBorrow(BORROW);
         jedisPool = new JedisPool(config, IP, PORT, TIMEOUT);
    }

    public JedisPool getJedisPool(){
        return jedisPool;
    }

    /**
     * 字符串set
     *
     * @param key
     * @param value
     * @return
     */
    public  boolean set(String key, String value) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            String status = jedis.set(key, value);
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis set 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }



    /**
     * 字符串set
     *
     * @param key
     * @param value
     * @param seconds
     *            单位秒,大于0
     * @return
     */
    public  boolean set(String key, String value, int seconds) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            String status = jedis.setex(key, seconds, value);
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis set 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    public  boolean set(byte[] key, int seconds, byte[] value) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            String status = jedis.setex(key, seconds, value);
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis set 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    /**
     * 是否存在key
     *
     * @param key
     * @return
     */
    public  boolean isExists(String key) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            return jedis.exists(key);
        } catch (Exception e) {
            logger.error("redis isExists 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    /**
     * 字符串 删除
     *
     * @param key
     * @return
     */
    public  boolean del(byte[] key) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            Long status = jedis.del(key);
            if (Objects.equals(SUCCESS_STATUS_LONG,status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis del 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    public  boolean del(String key) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            Long status = jedis.del(key);
            if (Objects.equals(SUCCESS_STATUS_LONG,status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis del 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    /**
     * 字符串获取
     *
     * @param key
     * @return
     */
    public  String get(String key) {
        String ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            ret = jedis.get(key);
        } catch (Exception e) {
            logger.error("redis get 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    public Long ttl(String key){
        Long ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            ret = jedis.ttl(key);
        } catch (Exception e) {
            logger.error("redis get 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    public  byte[] get(byte[] key) {
        byte[] ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            ret = jedis.get(key);
        } catch (Exception e) {
            logger.error("redis get 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * 获取分布式锁
     *
     * @param lockKey
     *            key为锁
     * @param requestId
     *            加锁请求
     * @param expireTime
     *            key的过期时间
     * @return
     */
    public  boolean getLock(String lockKey, String requestId, int expireTime) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            String status = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis 获取分布式锁 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey
     * @param requestId
     */
    public  boolean releaseLock(String lockKey, String requestId) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
			/*
			 * 其他请求误解锁问题 if(requestId.equals(jedis.get(lockKey))) { jedis.del(lockKey); }
			 */

            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object status = jedis.eval(script, Collections.singletonList(lockKey),
                    Collections.singletonList(requestId));
            if (SUCCESS_STATUS_LONG.equals(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis 释放分布式锁 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
        return ret;
    }

    /**
     * 序列化存入对象
     *
     * @param key
     * @param obj
     * @return
     */
    public  boolean set(byte[] key, Object obj) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            String status = jedis.set(key, baos.toByteArray());
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis set 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;

    }

    /**
     * 取序列化对象
     *
     * @param key
     * @return
     */
    public  Object getObj(byte[] key) {
        Object ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }

            byte[] rets = jedis.get(key);
            ByteArrayInputStream bais = new ByteArrayInputStream(rets);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            logger.error("redis get 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * hash数据类型存储对象
     *
     * @param key
     * @param obj
     * @return
     */
    public  boolean setHm(String key, Object obj) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            Map<String, String> hash = objToMap(obj);
            String status = jedis.hmset(key, hash);
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis setHm 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * 修改对象属性
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public  boolean setHm(String key, String field, String value) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }

            Long status = jedis.hset(key, field, value);
            if (0L == status) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis setHm 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * 根据fields 查询key对象属性列表
     *
     * @param key
     * @param fields
     * @return
     */
    public List<String> getHm(String key, String... fields) {
        List<String> ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return null;
            }
            ret = jedis.hmget(key, fields);
        } catch (Exception e) {
            logger.error("redis getHm 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * 根据field 查询key对象属性
     *
     * @param key
     * @param
     * @return
     */
    public  String getHm(String key, String field) {
        String ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return null;
            }

            ret = jedis.hget(key, field);

        } catch (Exception e) {
            logger.error("redis getHm 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }



    /**
     * json格式存对象
     * @param key
     * @param obj
     * @return
     */
    public  boolean setJson(String key,Object obj) {
        boolean ret = false;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            ObjectMapper mapper = new ObjectMapper();
            String status = jedis.set(key, mapper.writeValueAsString(obj));
            if (SUCCESS_OK.equalsIgnoreCase(status)) {
                ret = true;
            }
        } catch (Exception e) {
            logger.error("redis setJson 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;
    }

    /**
     * json格式取对象
     * @param key
     * @param clazz
     * @return
     */
    public  Object getJson(String key,Class clazz) {
        Object ret = null;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (jedis == null) {
                return ret;
            }
            String str = jedis.get(key);
            ObjectMapper mapper = new ObjectMapper();
            ret = mapper.readValue(str, clazz);
        } catch (Exception e) {
            logger.error("redis getJson 出错", e);
            jedisPool.returnBrokenResource(jedis);
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }

        return ret;

    }

    private HashMap<String, String> objToMap(Object obj) {

        if(null == obj) {
            return null;
        }
        HashMap<String, String> map = new HashMap<String, String>();
        Field[] fields = obj.getClass().getDeclaredFields();
        try {
            for (int i = 0; i < fields.length; i++) {
                String varName = fields[i].getName();
                boolean accessFlag = fields[i].isAccessible();
                fields[i].setAccessible(true);

                Object o = fields[i].get(obj);
                if (o != null) {
                    map.put(varName, o.toString());
                }

                fields[i].setAccessible(accessFlag);
            }
        } catch (Exception e) {

        }

        return map;
    }

    /**
     * 设置一个key的过期的秒数
     *
     * @param keyFormat
     *            key标识
     * @param seconds
     *            过期的秒数
     * @param keyValues
     *            key变量
     * @return 1表示设置成功, 0 表示设置失败或者无法被设置
     */
    public Long expire(String key, int seconds) {
        Jedis jedis = null;
        try {
            jedis =jedisPool.getResource();
            return jedis.expire(key, seconds);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

4)token管理器JwtProvider

package com.demo.util;

import com.demo.constants.Constants;
import com.demo.exception.TokenException;
import com.demo.factory.ServiceFactory;
import io.jsonwebtoken.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * token管理器
 */
public class JwtProvider {

    private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class);
    private static  RedisClient redisClient;

    static {
        redisClient = ServiceFactory.getInstance().createRedisClient();
    }

    /**
     * 生成token并将token写入redis
     * @param res
     * @param username
     * @return
     */
    public static String addAuthentication(HttpServletResponse res, String username){
        String jwtStr = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + Constants.EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, Constants.SECRET)
                .compact();
        String tokent = jwtStr;
        res.addHeader(Constants.HEADER_STRING,tokent );
        //每次重置token,实现单点登录
        redisClient.set(Constants.TOKEN_JWT+username,tokent);
        return tokent;
    }

    /**
     * 从token中获取username
     * @param req
     * @return
     */
    public static String getAuthentication(HttpServletRequest req){
        String token = req.getHeader(Constants.HEADER_STRING);
        String username = null;
        if(!StringUtils.isEmpty(token)){
            try{
                // parse the jwt
                username = Jwts.parser()
                        .setSigningKey(Constants.SECRET)
                        .parseClaimsJws(token)
                        .getBody()
                        .getSubject();
            }catch (ExpiredJwtException e) {
                logger.error("Token已过期: {} " + e);
                throw new TokenException("Token已过期");
            } catch (UnsupportedJwtException e) {
                logger.error("Token格式错误: {} " + e);
                throw new TokenException("Token格式错误");
            } catch (MalformedJwtException e) {
                logger.error("Token没有被正确构造: {} " + e);
                throw new TokenException("Token没有被正确构造");
            } catch (SignatureException e) {
                logger.error("签名失败: {} " + e);
                throw new TokenException("签名失败");
            } catch (IllegalArgumentException e) {
                logger.error("非法参数异常: {} " + e);
                throw new TokenException("非法参数异常");
            }
        }
        return username;
    }
}

8、security:全局security配置WebSecurityConfig:

package com.demo.security;


import com.demo.filter.JWTAuthenticationFilter;
import com.demo.filter.JWTLoginFilter;
import com.demo.handle.CustomAuthenticationFailureHandler;
import com.demo.handle.Http401AuthenticationEntryPoint;
import com.demo.service.UserService;
import com.demo.service.impl.CustomAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
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.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * SpringSecurity的配置
 * 通过SpringSecurity的配置,将JWTLoginFilter,JWTAuthenticationFilter组合在一起
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**白名单
     * 需要放行的URL
     */
    private static final String[] AUTH_WHITELIST = {
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/static",
            "/static/diagram-viewer",
            "/static/**",
            "/index.html",
            "/code/**",  //验证码

    };

    @Autowired
    private UserService userService;
//    @Autowired
//    private UserDetailsService jwtUserService;

//    @Autowired
//    private CustomAccessDeniedHandler customAccessDeniedHandler;
    @Autowired
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;


    // 设置 HTTP 验证规则
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.cors().and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated()  // 所有请求需要身份认证
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(
                        new Http401AuthenticationEntryPoint())
                .and()
                .addFilterBefore(new JWTLoginFilter(authenticationManager(),customAuthenticationFailureHandler,userService), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JWTAuthenticationFilter(authenticationManager(),userService), UsernamePasswordAuthenticationFilter.class);
        //因为是spring整合了spring Security,本例加了session共享,每次检查原session的时候如果不存在的话,就会创建新的session,故需要手动设置
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);

    }

    // 该方法是登录的时候会进入
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义身份验证组件
        auth.authenticationProvider(new CustomAuthenticationProvider(userService));
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(AUTH_WHITELIST);
    }

}

9、拦截器:

1)登录拦截器:

package com.demo.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.demo.constants.CommonEnums;
import com.demo.constants.Constants;
import com.demo.dto.JwtAuthenticationRequest;
import com.demo.dto.ResponseMessage;
import com.demo.factory.ServiceFactory;
import com.demo.handle.CustomAuthenticationFailureHandler;
import com.demo.service.UserService;
import com.demo.util.JwtProvider;
import com.demo.util.RedisClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;

/**登录接口(/login)拦截
 *
 * 验证用户名密码正确后,生成一个token,并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter,重写了其中的2个方法
 * attemptAuthentication :接收并解析用户凭证。
 * successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成token。
 */
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter{

    private static Logger logger = LoggerFactory.getLogger(JWTLoginFilter.class);
    private AuthenticationManager authenticationManager;
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    private UserService userService;
    private static  RedisClient redisClient;

    static {
        redisClient = ServiceFactory.getInstance().createRedisClient();
    }



    public JWTLoginFilter(AuthenticationManager authenticationManager,
                          CustomAuthenticationFailureHandler customAuthenticationFailureHandler,
                          UserService userService) {
        this.authenticationManager = authenticationManager;
        this.customAuthenticationFailureHandler = customAuthenticationFailureHandler;
        this.userService = userService;
        setAuthenticationManager(authenticationManager);
    }

    // 接收并解析用户凭证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
        String userName = "";
        HttpSession httpSession = req.getSession();

        try {
            JwtAuthenticationRequest user = new ObjectMapper().readValue(req.getInputStream(), JwtAuthenticationRequest.class);

            //判断用户是否多次登录失败被锁定
            String lock = redisClient.get(Constants.LOCK+user.getUsername());
            if (StringUtils.isNotEmpty(lock)) {
                httpSession.setAttribute("code",null);
                Long time = redisClient.ttl(Constants.LOCK+user.getUsername());
                res.setContentType(ContentType.APPLICATION_JSON.toString());
                res.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_305.code, CommonEnums.CODE_305.message+":"+time+"秒后解锁")));
                return null;
            }


                //校验验证码
            if (StringUtils.isNotEmpty(user.getCode())){
                if (!Objects.equals(user.getCode(),"1111")){
                    String code = (String) httpSession.getAttribute("code");
                    if(StringUtils.isEmpty(code)){
                        httpSession.setAttribute("code",null);
                        res.setContentType(ContentType.APPLICATION_JSON.toString());
                        res.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_405.code,CommonEnums.CODE_405.message)));
                        return null;
                    }
                    if (!Objects.equals(user.getCode().toLowerCase(),code.toLowerCase())){
                        httpSession.setAttribute("code",null);
                        res.setContentType(ContentType.APPLICATION_JSON.toString());
                        res.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_403.code,CommonEnums.CODE_403.message)));
                        return null;
                    }
                }
            }else{
                httpSession.setAttribute("code",null);
                res.setContentType(ContentType.APPLICATION_JSON.toString());
                res.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_405.code,CommonEnums.CODE_405.message)));
            }
            userName = user.getUsername();
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            user.getUsername(),
                            user.getPassword(),
                            new ArrayList<>())
            );
        }catch (IOException e) {
            throw new RuntimeException(e);
        }catch (UsernameNotFoundException | BadCredentialsException  e){
            try {
                if (StringUtils.isNotEmpty(userName)){
                    String errorNum = redisClient.get(Constants.ERROR_NUM+userName);
                    if (StringUtils.isNotEmpty(errorNum)){
                        if (Objects.equals(errorNum,"3")){
                            httpSession.setAttribute("code",null);
                            redisClient.set(Constants.LOCK+userName,"true",120);
                            res.setContentType(ContentType.APPLICATION_JSON.toString());
                            res.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_305.code,CommonEnums.CODE_305.message+":120秒后解锁")));
                        }else{
                            httpSession.setAttribute("code",null);
                            redisClient.set(Constants.ERROR_NUM+userName,String.valueOf(Integer.valueOf(errorNum)+1),120);
                            customAuthenticationFailureHandler.onAuthenticationFailure(req,res,e);
                        }
                    }else{
                        httpSession.setAttribute("code",null);
                        redisClient.set(Constants.ERROR_NUM+userName,"1",120);
                        customAuthenticationFailureHandler.onAuthenticationFailure(req,res,e);
                    }
                }else{
                    httpSession.setAttribute("code",null);
                    customAuthenticationFailureHandler.onAuthenticationFailure(req,res,e);
                }
            } catch (Exception e1) {
                throw new RuntimeException(e);
            }
            return null;
        }
    }

    // 用户成功登录后,这个方法会被调用,我们在这个方法里生成token
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute("code",null);
        redisClient.del(Constants.LOCK+auth.getName());
        redisClient.del(Constants.ERROR_NUM+auth.getName());
        String token = JwtProvider.addAuthentication(response, auth.getName());
        UserDetails userDetails = userService.loadUserByUsername(auth.getName());
        redisClient.set(Constants.JWT_USER+auth.getName(), JSONObject.toJSONString(userDetails));
        response.setContentType(ContentType.APPLICATION_JSON.toString());
        response.getWriter().write(JSON.toJSONString(ResponseMessage.success(CommonEnums.CODE_300.code,CommonEnums.CODE_300.message,token)));
    }

}

2)非登录拦截器:

package com.demo.filter;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.demo.constants.CommonEnums;
import com.demo.constants.Constants;
import com.demo.dto.JwtUser;
import com.demo.dto.ResponseMessage;
import com.demo.factory.ServiceFactory;
import com.demo.service.UserService;
import com.demo.util.JwtProvider;
import com.demo.util.RedisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
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.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import org.apache.http.entity.ContentType;

/**非登录接口拦截
 *
 * 自定义JWT认证过滤器
 * 该类继承自BasicAuthenticationFilter,在doFilterInternal方法中,
 * 从http头的Authorization 项读取token数据,然后用Jwts包提供的方法校验token的合法性。
 * 如果校验通过,就认为这是一个取得授权的合法请求
 */
public class JWTAuthenticationFilter extends BasicAuthenticationFilter{

    private static final Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);

    private static  RedisClient redisClient;

    static {
        redisClient = ServiceFactory.getInstance().createRedisClient();
    }
    private UserService userService;


    public JWTAuthenticationFilter(AuthenticationManager authenticationManager,
                                   UserService userService) {
        super(authenticationManager);
        this.userService = userService;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        String token = request.getHeader(Constants.HEADER_STRING);
        String username = JwtProvider.getAuthentication(request);
        if(StringUtils.isEmpty(username)){
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Authentication Failed: username not found");
            return;
        }

        //效验token是否过期

        String tokenReids = redisClient.get(Constants.TOKEN_JWT+username);
        if (StringUtils.isEmpty(tokenReids)){
            logger.error("Token已过期: {} ",token);
            response.setContentType(ContentType.APPLICATION_JSON.toString());
            response.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_401.code,"Token已过期")));
            return;
        }

        if (!Objects.equals(tokenReids,token)){
            logger.error("Token不匹配: {} " + tokenReids);
            response.setContentType(ContentType.APPLICATION_JSON.toString());
            response.getWriter().write(JSON.toJSONString(ResponseMessage.error(CommonEnums.CODE_401.code,"Token不匹配")));
            return;
        }

       // if(SecurityContextHolder.getContext().getAuthentication() == null) {
            String userJson = redisClient.get(Constants.JWT_USER+username);
            UserDetails userDetails = null;
            if (!StringUtils.isEmpty(userJson)){
                JwtUser jwtUser =  JSONObject.parseObject(userJson,JwtUser.class);
                userDetails = jwtUser;
            }else{
                userDetails = userService.loadUserByUsername(username);
                redisClient.set(Constants.JWT_USER+username, JSONObject.toJSONString(userDetails));
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
       // }
        chain.doFilter(request, response);

    }
}

10、rest:

1)

package com.demo.rest;

import com.demo.dto.ResponseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Random;

@RestController
@RequestMapping("/code")
public class CodeController {

    private static Logger logger = LoggerFactory.getLogger(CodeController.class);

    /**
     * 获取验证码
     */
    @GetMapping(value = "/getCode")
    public ResponseMessage images(HttpServletRequest request, HttpServletResponse response) {

        try {
            HttpSession httpSession = request.getSession();
            Random r = new Random();
            String code = String.valueOf(r.nextInt(100));
            httpSession.setAttribute("code",code);
            return ResponseMessage.success("验证码",code);
        } catch (Exception e) {
            logger.error("ValidateCodeController images ERROR MESSAGE={}", e.getMessage(), e);
        }
        return ResponseMessage.error("");
    }
}

2):

package com.demo.rest;

import com.demo.constants.CommonEnums;
import com.demo.constants.Constants;
import com.demo.dto.AuthorityDTO;
import com.demo.dto.JwtUser;
import com.demo.dto.ResponseMessage;
import com.demo.dto.UserDTO;
import com.demo.service.UserService;
import com.demo.util.RedisClient;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/user")
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisClient redisClient;

    /**
     * 获取所有用户
     * @return
     */
    @RequestMapping("/getAllUsers")
    public ResponseMessage getAllUsers(){
        List<UserDTO>  users = userService.getAllUsers();
        return ResponseMessage.success(users);
    }

    /**
     * 获取权限
     */
    @RequestMapping("/getMyAuthorities")
    public ResponseMessage  getMyAuthorities(){
        JwtUser jwtUser = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Integer currentUserId = jwtUser.getId();
        List<AuthorityDTO> authorityDTOS= userService.getAuthortiesByUserId(currentUserId);
        return ResponseMessage.success(authorityDTOS);
    }

    @GetMapping(value = "/logout")
    public ResponseMessage logout() {
        try{
            JwtUser jwtUser = (JwtUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            String userName = jwtUser.getUsername();
            if (StringUtils.isEmpty(userName)){
                throw new RuntimeException(CommonEnums.CODE_303.message);
            }
            redisClient.del(Constants.TOKEN_JWT+userName);
        }catch (Exception e){

        }
        return ResponseMessage.success(CommonEnums.CODE_302.code,CommonEnums.CODE_302.message);
    }
}

11、启动类:

package com.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
@MapperScan("com.demo.dao")
public class SecurityApplication extends SpringBootServletInitializer {

    public static void main(String args[]){
        SpringApplication.run(SecurityApplication.class, args);
  }

}

三、说明:

WebSecurityConfig继承了WebSecurityConfigurerAdapter,为配置入口,
1、白名单直接放行,
2、非白名单:
   (1)登录(/login)接口:
      登录拦截JWTLoginFilter.attemptAuthentication-->
      自定义登录CustomAuthenticationProvider-->
      登录成功返回token JWTLoginFilter.successfulAuthentication
   (2)非登录接口:
      拦截器JWTAuthenticationFilter校验token

2、单一点登录原理:

 在JWTLoginFilter中可以看到,登录成功后需要返回token给前端,这个token是从addAuthentication方法获取的,JWT+redis可以实现token和用户id的互相取值,既可以根据token获取用户id,也可以根据用户id获取token。

每次登录会刷新token,再次用原来的token调用系统接口token验证失败,前端一般会跳转到登录页,由此实现单一点登录。 

由此,如果需要多点登录,每次登录成功后不重置token即可:

只需要这样修改:

Constants改下类型:
public static final int EXPIRATIONTIME = 1000 * 60 * 60 * 24 * 1; //单位毫秒,1 days

JWTProvider生成token的方法:

 /**
     * 生成token并将token写入redis
     * @param res
     * @param username
     * @return
     */
    public static String addAuthentication(HttpServletResponse res, String username){
        String oldToken = redisClient.get(Constants.TOKEN_JWT+username);
        if(StringUtils.isNotEmpty(oldToken)){
            return oldToken;
        }


        String jwtStr = Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + Constants.EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, Constants.SECRET)
                .compact();
        String tokent = jwtStr;
        res.addHeader(Constants.HEADER_STRING,tokent );
        //每次重置token,实现单点登录
        redisClient.set(Constants.TOKEN_JWT+username,tokent,Constants.EXPIRATIONTIME/1000);
        return tokent;
    }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Spring Security 是一个强大的开源框架,用于提供全面的身份验证(Authentication)和授权(Authorization)解决方案,常用于Java Web应用程序中保护资源访问。它简化了处理用户登录、权限控制以及会话管理等安全相关的任务。 简单授权Demo通常涉及以下几个步骤: 1. 添加依赖:首先在你的Spring Boot项目中添加Spring Security的依赖。如果你使用Maven,可以在pom.xml中添加: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 配置WebSecurityConfigurerAdapter:创建一个`WebSecurityConfigurerAdapter`子类,并覆盖`configure(HttpSecurity http)`方法,设置基本的安全配置,如登录页面、默认登录处理器等: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login", "/register").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .failureUrl("/login?error") .and() .logout() .logoutSuccessUrl("/") .deleteCookies("JSESSIONID"); } @Bean public UserDetailsService userDetailsServiceBean() { // 注册用户服务,这里是示例,实际应用中可能需要从数据库获取 return new InMemoryUserDetailsManager(Arrays.asList( new User("user", "password", AuthorityUtils.createAuthorityList("USER")) )); } } ``` 在这个例子中,我们允许"/login"和"/register"路径无需身份验证,所有其他请求都需要认证。登录失败后会被重定向到"/login?error"。 3. 用户认证:定义一个`UserDetailsService`实现,它负责从数据库或内存中加载用户信息进行验证。这里使用的是内存中的简单例子。 4. 创建Controller和视图:定义一个控制器,处理登录请求并渲染登录页面。例如: ```java @Controller public class LoginController { @GetMapping("/login") public String loginPage() { return "login"; } @PostMapping("/login") public String handleLogin(@RequestParam String username, @RequestParam String password, Model model) { // 这里只是一个简单的示例,实际应用中应检查用户名密码是否正确 if ("user".equals(username) && "password".equals(password)) { return "redirect:/"; } else { model.addAttribute("error", "Invalid credentials"); return "login"; } } } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值