SpringBoot 整合security 实现自定义Token和clientId登录及退出(一)

1.数据库创建
user表
在这里插入图片描述
user_token表
在这里插入图片描述

2.创建对Token和用户信息操作的service及实体类
话不多说,直接上代码
TokenInfo.java

/**
 * Token信息
 */
public class TokenInfo {
    private Integer userId;
    private String accessToken;
    private String clientId;
    private String ip;
    private LocalDateTime updateTime;
    ...

userInfo

/**
 * 用户信息
 */
public class UserInfo {
    private String userId;
    private String username;
    private String role;
    private String realName;
    private String password;
    ...

service(实现类,接口就不写了)
TokenServiceImpl.java

@Service
@SuppressWarnings("all")
public class TokenServiceImpl implements TokenService {

    @Autowired
    private TokenMapper tokenMapper;

    /**
     * 过期时间5分钟
     */
    private static final long EXPIRE_TIME = 5 * 60 * 1000;

    /**
     * 加密密钥
     */
    private static final String KEY = "demo";

    @Override
    public TokenInfo findByUserId(String userId) {
        return tokenMapper.findByUserId(userId);
    }

    @Override
    public TokenInfo findByClientId(String clientId) {
        return tokenMapper.findByClientId(clientId);
    }

    @Override
    public TokenInfo createToken(UserDetailsInfo userInfo) {
        TokenInfo tokenInfo = new TokenInfo();
        Map<String, Object> header = new HashMap();
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        //setID:用户ID
        //setExpiration:token过期时间  当前时间+有效时间
        //setSubject:用户名
        //setIssuedAt:token创建时间
        //signWith:加密方式
        JwtBuilder builder = Jwts.builder().setHeader(header)
            .setId(userInfo.getUserId())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME))
            .setSubject(userInfo.getUsername())
            .setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256, KEY);
        tokenInfo.setAccessToken(builder.compact());
        tokenInfo.setIp(userInfo.getIp());
        tokenInfo.setUserId(Integer.valueOf(userInfo.getUserId()));
        String clientId = "";
        // 根据数据库的用户信息查询Token
        TokenInfo accesstoken = tokenMapper.findByUserId(userInfo.getUserId());
        if (accesstoken != null && equal(accesstoken.getIp(), userInfo.getIp())) {
            clientId = accesstoken.getClientId();
            tokenInfo.setClientId(clientId);
            // 更新Token信息
            tokenMapper.updateToken(tokenInfo);
        } else {
            clientId = UUID.randomUUID().toString();
            tokenInfo.setClientId(clientId);
            // 登陆Token信息
            tokenMapper.addToken(tokenInfo);
        }
        return tokenInfo;
    }


    @Override
    public void updateToken(TokenInfo token) {
        tokenMapper.updateToken(token);
    }

    @Override
    public void deleteToken(String clientId) {
        tokenMapper.deleteToken(clientId);
    }

    @Override
    public boolean checkToken(TokenInfo tokenInfo) {

        //根据ClientId查找数据库Token
        TokenInfo myToken = tokenMapper.findByClientId(tokenInfo.getClientId());
        if (myToken != null && tokenInfo.getAccessToken().
            equals(myToken.getAccessToken())) {
            Claims claims = null;
            try {
                //token过期后,会抛出ExpiredJwtException 异常,通过这个来判定token过期,
                claims = Jwts.parser().setSigningKey(KEY).parseClaimsJws(tokenInfo.getAccessToken()).getBody();
            } catch (ExpiredJwtException e) {
                return false;
            }
            return true;
        }
        return false;
    }

}

UserServiceImpl.java

@Service
@SuppressWarnings("all")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserInfo findByUserId(Integer userId) {
        return userMapper.findByUserId(userId);
    }

    @Override
    public UserInfo findByUserName(String userName) {
        return userMapper.findByUserName(userName);
    }

    @Override
    public List<UserInfo> selectAll() {
        return userMapper.selectAll();
    }
}

3.工具类创建
SecurityUtils.java

/**
 * Security工具类
 */
public class SecurityUtils {

    private static final String MD5_KEY = "0123456789AbCDeF";

    /**
     * 获取当前登录信息
     * @return
     */
    public static Authentication getAuthentication() {
        if (SecurityContextHolder.getContext() != null) {
            return SecurityContextHolder.getContext().getAuthentication();
        }
        return null;
    }

    public static void setAuthentication(Authentication authentication) {
        if (SecurityContextHolder.getContext() != null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    }

    public static Integer getUserId() {
        Integer userId = null;
        Authentication authentication = getAuthentication();
        if (authentication != null) {
            Object principal = authentication.getPrincipal();
            if (principal != null && principal instanceof UserDetailsInfo) {
                userId = Integer.valueOf(((UserDetailsInfo) principal).getUserId());
            }
        }
        return userId;
    }
    
    public static String getUsername() {
        String userName = null;
        Authentication authentication = getAuthentication();
        if (authentication != null) {
            Object principal = authentication.getPrincipal();
            if (principal != null && principal instanceof UserDetails) {
                userName = ((UserDetails) principal).getUsername();
            }
        }
        return userName;
    }

    public static boolean hasRole(String role) {
        if (SecurityContextHolder.getContext() == null) {
            return false;
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
            if (role.equals(grantedAuthority.getAuthority())) {
                return true;
            }
        }
        return false;
    }

}

ClientIdUtil.java

public class ClientIdUtil {
    /**
     * 用户ip获取
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.equals("0:0:0:0:0:0:0:1")) {
            ip = "127.0.0.1";
        }
        return ip;
    }
}

MD5Util.java

/**
 * MD5加密
 * @author:admin
 * @date:Created at 2020/12/13
 */
public class MD5Util {
    public static String MD5(String s) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(s.getBytes("utf-8"));
            return toHex(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String toHex(byte[] bytes) {
        final char[] HEX_DIGITS = "0123456789AbCDeF".toCharArray();
        StringBuilder ret = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
        }
        return ret.toString();
    }

    /**
     * 密码加强版
     *
     * @param str
     * @return
     */
    public static String MD55(String str) {
        String pwd1 = MD5Util.MD5(str);
        return pwd1.substring(0, 10) + pwd1.substring(22) + pwd1.substring(10, 22);

    }
}

4.实现UserDetailsService
UserDetailsInfo.java

public class UserDetailsInfo extends User {
    private String userId;
    private String role;
    private String realName;
    private String ip;
    ...

DemoUserDetailsService.java

/**
 * 通过用户名取得用户信息
 */
@Service
@SuppressWarnings("all")
public class DemoUserDetailsService implements UserDetailsService {
    @Autowired
    private UserService userService;
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        // 取得数据库中的用户信息
        UserInfo user = userService.findByUserName(userName);
        // 用户不存在
        if (user == null) {
            throw new UsernameNotFoundException("user is not exist");
        }
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getRole());
        authorities.add(grantedAuthority);
        // 设置UserDetailsInfo用户信息
        UserDetailsInfo userDetails = new UserDetailsInfo(user.getUserId(),
            user.getUsername(),user.getPassword(),authorities);
        return userDetails;
    }
}

4.用户身份验证
DemoAuthenticationProvider.java

/**
 * 用户身份验证
 * @author:zhuolun.he
 * @date:Created at 2020/12/13
 */
@Component
@SuppressWarnings("all")
public class DemoAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private DemoUserDetailsService demoUserDetailsService;

    /**
     * 自定义验证方式
     * @param authentication 认证信息
     * @return 用户认证信息
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
        //认证信息取得
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

        // 获取用户信息
        UserDetails user = demoUserDetailsService.loadUserByUsername(username);
        if (user ==null) {
            throw new BadCredentialsException("Username not found");
        }
        try {
            // 验证密码(MD5加密)
            password = MD5Util.MD5(password);
        } catch (Exception e){
            throw new BadCredentialsException("Password encode failed");
        }
        // 判断用户密码是否正确
        if (!password.equals(user.getPassword())) {
            throw new BadCredentialsException("Wrong password");
        }
        // 用户认证信息设定
        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        UsernamePasswordAuthenticationToken userInfo = new
            UsernamePasswordAuthenticationToken(user,password,authorities);
        userInfo.setDetails(user);
        return userInfo;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

5.过滤器创建
SecurityFilter.java

/**
 * 过滤器
 */
public class SecurityFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger(SecurityFilter.class);

    @Autowired
    private TokenService tokenService;

    /**
     * 放行的url:login/logout
     */
    @Value("${spring.security.url}")
    private String[] urls;

    private static final String FILTER_APPLIED = "_spring_security_filterSecurityInterceptor_filterApplied";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("init in Security");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (request.getAttribute(FILTER_APPLIED) != null) {
            filterChain.doFilter(request, response);
            return;
        }
        request.setAttribute(FILTER_APPLIED, true);
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        // url放行
        for (String doUrl : urls) {
            if (httpRequest.getRequestURI().endsWith(doUrl)) {
                filterChain.doFilter(request, response);
                return;
            }
        }
        // 用户信息过滤
        Object userInfo = SecurityUtils.getAuthentication().getPrincipal();
        if (userInfo == null || !(userInfo instanceof UserDetailsInfo)) {
            response.setContentType("application/json;charset=UTF-8");
            ResultData data = new ResultData(0,"Security authentication invalid");
            response.getWriter().write((new Gson()).toJson(data));
            return;
        }
        // clientId和accessToken取得
        String clientId = httpRequest.getHeader("clientId");
        String accessToken = httpRequest.getHeader("accessToken");
        TokenInfo tokenInfo = new TokenInfo();
        tokenInfo.setClientId(clientId);
        tokenInfo.setAccessToken(accessToken);
        // 对Token验证过滤
        if (!tokenService.checkToken(tokenInfo)) {
            response.setContentType("application/json;charset=UTF-8");
            ResultData data = new ResultData(0,"Security authentication invalid");
            response.getWriter().write((new Gson()).toJson(data));
            return;
        }
        filterChain.doFilter(request, response);
    }
    
    @Override
    public void destroy() {}
}
posted on 2020-12-19 23:33  猫的树kireCat  阅读(559)  评论(0编辑  收藏  举报