1. 程式人生 > 其它 >SpringBoot 整合security 實現自定義Token和clientId登入及退出(一)

SpringBoot 整合security 實現自定義Token和clientId登入及退出(一)

技術標籤:securitytokenspring bootspringjava

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() {}
}