SpringBoot 整合security 實現自定義Token和clientId登入及退出(一)
阿新 • • 發佈:2020-12-20
技術標籤: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() {}
}